PlayerMovement And CameraFollow - c#

I am making a thirdperson Unity game. I think that I'm almost done but I just can't figure out how to make so the WASD control is relative to the camera?
Right now when I'm moving with WASD it works, but if i start to rotate the camera by using mouse, suddenly my (W) button makes the player walk backwards and left button (A) goes right. All controls are just bugging.
This is my PlayerMovement.cs and CameraFollow.cs:
PlayerMovement:
public float speed = 6f; // The speed that the player will move at.
Vector3 movement; // The vector to store the direction of the player's movement.
Animator anim; // Reference to the animator component.
Rigidbody playerRigidbody; // Reference to the player's rigidbody.
int floorMask; // A layer mask so that a ray can be cast just at gameobjects on the floor layer.
float camRayLength = 100f; // The length of the ray from the camera into the scene.
void Awake ()
{
// Create a layer mask for the floor layer.
floorMask = LayerMask.GetMask ("Floor");
// Set up references.
anim = GetComponent <Animator> ();
playerRigidbody = GetComponent <Rigidbody> ();
}
void FixedUpdate ()
{
// Store the input axes.
float h = CrossPlatformInputManager.GetAxisRaw("Horizontal");
float v = CrossPlatformInputManager.GetAxisRaw("Vertical");
// Move the player around the scene.
Move (h, v);
// Turn the player to face the mouse cursor.
Turning ();
// Animate the player.
Animating (h, v);
Shooting ();
}
void Move (float h, float v)
{
// Set the movement vector based on the axis input.
movement.Set (h, 0f, v);
// Normalise the movement vector and make it proportional to the speed per second.
movement = movement.normalized * speed * Time.deltaTime;
// Move the player to it's current position plus the movement.
playerRigidbody.MovePosition (transform.position + movement);
}
void Turning ()
{
// Create a ray from the mouse cursor on screen in the direction of the camera.
Ray camRay = Camera.main.ScreenPointToRay (Input.mousePosition);
// Create a RaycastHit variable to store information about what was hit by the ray.
RaycastHit floorHit;
// Perform the raycast and if it hits something on the floor layer...
if(Physics.Raycast (camRay, out floorHit, camRayLength, floorMask))
{
// Create a vector from the player to the point on the floor the raycast from the mouse hit.
Vector3 playerToMouse = floorHit.point - transform.position;
// Ensure the vector is entirely along the floor plane.
playerToMouse.y = 0f;
// Create a quaternion (rotation) based on looking down the vector from the player to the mouse.
Quaternion newRotatation = Quaternion.LookRotation (playerToMouse);
// Set the player's rotation to this new rotation.
playerRigidbody.MoveRotation (newRotatation);
}
}
void Animating (float h, float v)
{
// Create a boolean that is true if either of the input axes is non-zero.
bool walking = h != 0f || v != 0f;
// Tell the animator whether or not the player is walking.
anim.SetBool ("IsWalking", walking);
}
void Shooting ()
{
// Create a boolean that is true if either of the input axes is non-zero.
bool shooting = (Input.GetMouseButtonDown(0));
// Tell the animator whether or not the player is walking.
anim.SetBool ("IsShooting", shooting);
}
CameraFollow:
// Camera target to look at.
public Transform target;
// Exposed vars for the camera position from the target.
public float height = 20f;
public float distance = 20f;
// Camera limits.
public float min = 10f;
public float max = 60;
// Rotation.
public float rotateSpeed = 1f;
// Options.
public bool doRotate;
public bool doZoom;
// The movement amount when zooming.
public float zoomStep = 30f;
public float zoomSpeed = 5f;
private float heightWanted;
private float distanceWanted;
// Result vectors.
private Vector3 zoomResult;
private Quaternion rotationResult;
private Vector3 targetAdjustedPosition;
void Start(){
// Initialise default zoom vals.
heightWanted = height;
distanceWanted = distance;
// Setup our default camera. We set the zoom result to be our default position.
zoomResult = new Vector3(0f, height, -distance);
}
void LateUpdate(){
// Check target.
if( !target ){
Debug.LogError("This camera has no target, you need to assign a target in the inspector.");
return;
}
if( doZoom ){
// Record our mouse input. If we zoom add this to our height and distance.
float mouseInput = Input.GetAxis("Mouse ScrollWheel");
heightWanted -= zoomStep * mouseInput;
distanceWanted -= zoomStep * mouseInput;
// Make sure they meet our min/max values.
heightWanted = Mathf.Clamp(heightWanted, min, max);
distanceWanted = Mathf.Clamp(distanceWanted, min, max);
height = Mathf.Lerp(height, heightWanted, Time.deltaTime * zoomSpeed);
distance = Mathf.Lerp(distance, distanceWanted, Time.deltaTime * zoomSpeed);
// Post our result.
zoomResult = new Vector3(0f, height, -distance);
}
if( doRotate ){
// Work out the current and wanted rots.
float currentRotationAngle = transform.eulerAngles.y;
float wantedRotationAngle = target.eulerAngles.y;
// Smooth the rotation.
currentRotationAngle = Mathf.LerpAngle(currentRotationAngle, wantedRotationAngle, rotateSpeed * Time.deltaTime);
// Convert the angle into a rotation.
rotationResult = Quaternion.Euler(0f, currentRotationAngle, 0f);
}
// Set the camera position reference.
targetAdjustedPosition = rotationResult * zoomResult;
transform.position = target.position + targetAdjustedPosition;
// Face the desired position.
transform.LookAt(target);
}

Your 'movement' vector should not be independent of camera forward vector, if you want a normal 3-rd person movement. Instead of moving in the 'h' or 'v' directions, move in camera.right and camera.forward directions, multiplied by 'h' and 'v' magnitudes respectively.
To be more accurate, if your camera is slightly inclined (so that its forward will go downward), you should first project camera.forward onto [player.forward, player.right] plane. Unity has a helper function just for that (Vector3.ProjectOnPlane()). If your camera also rotates around its forward direction (e.g. camera shake), you should also project camera.right on the same plane.
// Set the movement vector based on the axis input.
movement.Set (camera.right * h, 0f, camera.forward * v);

Related

Camera follow the player only on his Z axis Unity 3D

I made the script bellow for the camera to follow the player, but I don't want it to follow him on his X axis. So it should follow the player smoothly only on his Z axis. Any suggestions?
Camera Script:
public Transform Ball;
public float SmoothSpeed = 0.125f;
public Vector3 offset;
bool MoveCamera = true;
void Start () {
}
public void FixedUpdate () {
if(MoveCamera == true){
Vector3 desiredPosition = Ball.position + offset;
Vector3 SmoothedPosition = Vector3.Lerp(transform.position, desiredPosition, SmoothSpeed);
transform.position = SmoothedPosition;
}
if (transform.position.z >= 2f)
{
MoveCamera = false;
}
}
What about only changing the z component?
public void FixedUpdate () {
if(MoveCamera == true){
Vector3 desiredPosition = new Vector3(0, Ball.position.y + offset.y, Ball.position.z + offset.z);
Vector3 SmoothedPosition = Vector3.Lerp(transform.position, desiredPosition, SmoothSpeed);
transform.position = SmoothedPosition;
}
if (transform.position.z >= 2f)
{
MoveCamera = false;
}
}
public gameObject toFollow;
Vector3 camDistance;
public void Start(){
camDistance = toFollow.transform.position-Camera.main.position;
}
public void Update(){
Vector3 following = new Vector3(Camera.main.x,Camera.main.z,toFollow.transform.position.z-Camera.main.position.z);
Camera.main.transform.position = following;
}
i just threw this together so it may require a few tweaks, but it should get you where you need to go. alternatively.... you can add a rigidbody to the camera, and in the inspector tick the boxes for constraints on the x and y axis. this will allow it to move only on the z (if you go this route remember to turn of the gravity for the cameras rigidbody as well)
Anybody in 2019 need a camera to be offset a specific position from their 3d character? Here is an easy solution! Attach this script to the camera you want to follow the player. Select the player as your target in the Unity editor.
This script sets the new position of the camera to
(EDIT: Where did the LaTeX go?)
$v = <targetPosition.x + xOffset, targetPosition.y + yOffset, targetPosition.z + zOffset>$
Benefits of this script:
Camera orientation does not change, even if character turns (because camera is not parented to the player).
Works regardless of player position
Limitations:
- Because camera orientation is fixed, you need to make sure that your character is not obscured by scenery.
- This style of camera control is best-suited for a 3rd person orthographic projection.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraController : MonoBehaviour
{
public Transform target;
Vector3 targetPos;
void Update()
{
targetPos = target.position;
transform.position = new Vector3(targetPos.x + 10f, targetPos.y + 8.571067812f, targetPos.z - 10f);
// transform.LookAt(target.transform);
}
}
My use-case required an orthographic projection. Orthographic angles are usually at a 30 degree angle of declination, so my offset is back 10 units, over 10 units, and up half that hypotenuse (that is, up 10*sqrt2 * 1/2). I also bumped the y-position up by the character's height (1.5 units), which I thought would give a better center for the camera target. This gives a y-offset of about 8.571067812.

Creating a Google Earth like navigation for a sphere

This question is about Unity3D.
I want to create a navigation similar to Google Earth where you click and drag on a sphere and let the camera orbit accordingly. It is important that the point that was grabbed is always under the mouse position while dragging. The navigation should also work if I zoom close to the sphere. I do not want to rotate the sphere itself. Just exactly like Google Earth does it.
My attempt is to project the mouse position to the sphere if I start to drag. On the next frame I do the same and calculate the angle between the start drag and end drag position.
private void RotateCamera(Vector3 dragStart, Vector3 dragEnd)
{
// calc the rotation of the drag
float angle = Vector3.Angle(dragStart, dragEnd);
// rotate the camera around the sphere
Camera.main.transform.RotateAround(sphere), Vector3.up, angle);
}
I thought of using Unitys RotateAround method to rotate the camera with the calculated angle. Unfortunately I do not have the rotation vector (using Vector3.up in the example is obviously wrong).
Does somebody know how I can calculate this vector to apply it for the method? Am I on the right direction to implement the Google Earth navigation?
Thank You!
UPDATE
I am very close with a new solution. I project the drag vectors to a down and a right plane to get the angles. Afterwards I rotate the camera around up and left. This works well until I reach the poles of the sphere. The camera rotates a lot around itself if I reach a pole.
private void RotateCamera(Vector3 dragStart, Vector3 dragEnd)
{
Vector3 plane = Vector3.down;
var a = Vector3.ProjectOnPlane(dragStart, plane);
var b = Vector3.ProjectOnPlane(dragEnd, plane);
float up = Vector3.SignedAngle(a, b, plane);
plane = Vector3.right;
a = Vector3.ProjectOnPlane(dragStart, plane);
b = Vector3.ProjectOnPlane(dragEnd, plane);
float left = Vector3.SignedAngle(a, b, plane);
Camera.main.transform.RotateAround(_sphere, Vector3.up, up);
Camera.main.transform.RotateAround(_sphere, Vector3.left, left);
}
Turns out that is was easier than I expected. I thought about calculating the rotation axis and came to the conclusion that is must be the cross product of the start and end vector. Take a look at the solution. The RotateCamera method is where the math magic happens :)
public class GoogleEarthControls : MonoBehaviour
{
private const int SpehreRadius = 1;
private Vector3? _mouseStartPos;
private Vector3? _currentMousePos;
void Start () {
// init the camera to look at this object
Vector3 cameraPos = new Vector3(
transform.position.x,
transform.position.y,
transform.position.z - 2);
Camera.main.transform.position = cameraPos;
Camera.main.transform.LookAt(transform.position);
}
private void Update()
{
if (Input.GetMouseButtonDown(0)) _mouseStartPos = GetMouseHit();
if (_mouseStartPos != null) HandleDrag();
if (Input.GetMouseButtonUp(0)) HandleDrop();
}
private void HandleDrag()
{
_currentMousePos = GetMouseHit();
RotateCamera((Vector3) _mouseStartPos, (Vector3)_currentMousePos);
}
private void HandleDrop()
{
_mouseStartPos = null;
_currentMousePos = null;
}
private void RotateCamera(Vector3 dragStartPosition, Vector3 dragEndPosition)
{
// in case the spehre model is not a perfect sphere..
dragEndPosition = dragEndPosition.normalized * SpehreRadius;
dragStartPosition = dragStartPosition.normalized * SpehreRadius;
// calc a vertical vector to rotate around..
var cross = Vector3.Cross(dragEndPosition, dragStartPosition);
// calc the angle for the rotation..
var angle = Vector3.SignedAngle(dragEndPosition, dragStartPosition, cross);
// roatate around the vector..
Camera.main.transform.RotateAround(transform.position, cross, angle);
}
/**
* Projects the mouse position to the sphere and returns the intersection point.
*/
private static Vector3? GetMouseHit()
{
// make sure there is a shepre mesh with a colider centered at this game object
// with a radius of SpehreRadius
RaycastHit hit;
if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out hit))
{
return hit.point;
}
return null;
}
}
Basic rotation based on mouse drag, based on what you have:
Transform camTransform = Camera.main.transform;
if (Input.GetMouseButton(0))
{
camTransform.RotateAround(currentLookTargetTransform.position, -camTransform.right * Input.GetAxis("Mouse Y") + camTransform.up * Input.GetAxis("Mouse X"), 120 * Time.deltaTime);
}
You can multiply the relative direction by the mouse change value to get the axis. Then you can supplant your clamp points in; but the point was to rotate it relatively.

Shooting a projectile but goes in wrong direction - 2D game

I am trying to make a simple game where I am shooting a projectile when the user touches the screen, I first spawn 8 projectiles (sprites) and when the user touches the screen I would like the top projectile to fly in the touch direction. I was able to do this; However, every time I shoot, the projectile goes in the wrong direction, here is an image which will illustrate the issue.
Obviously the image is still here but the object will continue flying until it goes out of the screen and then gets destroyed.
Here is the code snippet that handles this
GameplayController.cs
if (Input.GetMouseButtonDown(0))
{
Vector3 position = new Vector3(Input.mousePosition.x, Input.mousePosition.y, 0);
position = Camera.main.ScreenToWorldPoint(position);
GameObject target;
target = new GameObject();
target.transform.position = position;
Arrow comp = currentArrows[0].GetComponent<Arrow>();
comp.setTarget(target.transform);
comp.GetComponent<Arrow>().arrowSpeed = 12;
comp.GetComponent<Arrow>().shoot = true;
currentArrows.RemoveAt(0);
Destroy(target);
}
I know I am getting the mouse input here and not the phone touch and that's fine for me, later I will convert it to the touch input.
Arrow.cs
public bool shoot = false;
public float arrowSpeed = 0.0f;
public Vector3 myDir;
public float speed = 30.0f;
private Transform target;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
if(shoot)
{
transform.position += transform.right * arrowSpeed * Time.deltaTime;
}
}
public void setTarget(Transform targetTransform)
{
this.target = targetTransform;
Vector3 vectorToTarget = target.position - transform.position;
float angle = Mathf.Atan2(vectorToTarget.y, vectorToTarget.x) * Mathf.Rad2Deg;
Quaternion q = Quaternion.AngleAxis(angle, Vector3.forward);
transform.rotation = Quaternion.Slerp(transform.rotation, q, Time.deltaTime * speed);
}
private void OnBecameInvisible()
{
print("Disappeared");
Destroy(gameObject);
Gameplay.instance.isShooting = false;
}
Vector3 position = new Vector3(Input.mousePosition.x, Input.mousePosition.y, 0);
I think that your problem is that you're getting the screen coordinates by click, not the world coordinates, which is actually two different things. In order to direct your projectile correctly you need to convert your screen coordinates to world like it's done here.
The next thing is how you move the projectile:
transform.position += transform.right * arrowSpeed * Time.deltaTime;
You're moving the projectile to the right and then rotating it somehow. Maybe you should try to move it with Vector3.Lerp, which will be easier and rotate it with Transform.LookAt.

Character vibrates when movement starts unity

I'm working through a book and have come to an issue. it's a top down shooting game that has the player rotate with the mouse and move with the keyboard. problem is when testing if either the mouse or the keyboard set off movement the image vibrates. If i push the arrow keys it moves in a circle the longer I hold the key the wider the circle. Below is the script I'm working with.
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class PlayerBehaviour : MonoBehaviour
{
//movement modifier applied to directional movement
public float playerSpeed = 2.0f;
//current player speed
private float currentSpeed = 0.0f;
/*
* Allows us to have multiple inputs and supports keyboard,
* joystick, etc.
*/
public List<KeyCode> upButton;
public List<KeyCode> downButton;
public List<KeyCode> leftButton;
public List<KeyCode> rightButton;
//last movement made
private Vector3 lastMovement = new Vector3();
// Update is called once per frame
void Update()
{
//rotates ship to face mouse
Rotation();
//moves ship
Movement();
}
void Rotation()
{
//finds mouse in relation to player location
Vector3 worldPos = Input.mousePosition;
worldPos = Camera.main.ScreenToWorldPoint(worldPos);
/*
get x and y screen positions
*/
float dx = this.transform.position.x - worldPos.x;
float dy = this.transform.position.y - worldPos.y;
//find the angle between objects
float angle = Mathf.Atan2(dy, dx) * Mathf.Rad2Deg;
/*
* The transform's rotation property uses a Quaternion,
* so we need to convert the angle in a Vector
* (The Z axis is for rotation for 2D).
*/
Quaternion rot = Quaternion.Euler(new Vector3(0, 0, angle + 90));
// Assign the ship's rotation
this.transform.rotation = rot;
}
// Will move the player based off of keys pressed
void Movement()
{
// The movement that needs to occur this frame
Vector3 movement = new Vector3();
// Check for input
movement += MoveIfPressed(upButton, Vector3.up);
movement += MoveIfPressed(downButton, Vector3.down);
movement += MoveIfPressed(leftButton, Vector3.left);
movement += MoveIfPressed(rightButton, Vector3.right);
/*
* If we pressed multiple buttons, make sure we're only
* moving the same length.
*/
movement.Normalize();
// Check if we pressed anything
if (movement.magnitude > 0)
{
// If we did, move in that direction
currentSpeed = playerSpeed;
this.transform.Translate(movement * Time.deltaTime * playerSpeed, Space.World);
lastMovement = movement;
}
else
{
// Otherwise, move in the direction we were going
this.transform.Translate(lastMovement * Time.deltaTime * currentSpeed, Space.World);
// Slow down over time
currentSpeed *= .9f;
}
}
/*
* Will return the movement if any of the keys are pressed,
* otherwise it will return (0,0,0)
*/
Vector3 MoveIfPressed(List<KeyCode> keyList, Vector3 Movement)
{
// Check each key in our list
foreach (KeyCode element in keyList)
{
if (Input.GetKey(element))
{
/*
* It was pressed so we leave the function
* with the movement applied.
*/
return Movement;
}
}
// None of the keys were pressed, so don't need to move
return Vector3.zero;
}
}
I studied your code for a while and could not find anything wrong with it. So I tested it myself and it works perfectly.
So I suppose there's something wrong with your scene. You might for example have your player object be a child of some object that rotates your axes: this would cause problems I suppose.
Have a new, empty scene. Add a new GameObject (a 3D cube for example, or a 2D sprite) and assign PlayerBehaviour to it. Now test: it should work perfectly.

Timer unity3d col.gameObject.name

I try to create a timer can it start when the player destroy the object.
I need, when the player destroy the object call "test", start a timer and for 10 seconds the player speed is change, from 5f to 10f. When the timer is 10 or more i need the player speed come back to 5f.
This is my code:
using UnityEngine;
using UnityEngine.UI;
using UnitySampleAssets.CrossPlatformInput;
namespace CompleteProject
{
public class PlayerMovement : MonoBehaviour
{
public static float speed = 5f;
public float timer = 5.0f;
float timerMin = 1.0f;
Vector3 movement; // The vector to store the direction of the player's movement.
Animator anim; // Reference to the animator component.
Rigidbody playerRigidbody; // Reference to the player's rigidbody.
#if !MOBILE_INPUT
int floorMask; // A layer mask so that a ray can be cast just at gameobjects on the floor layer.
float camRayLength = 100f; // The length of the ray from the camera into the scene.
#endif
void Awake ()
{
#if !MOBILE_INPUT
// Create a layer mask for the floor layer.
floorMask = LayerMask.GetMask ("Floor");
#endif
// Set up references.
anim = GetComponent <Animator> ();
playerRigidbody = GetComponent <Rigidbody> ();
}
void FixedUpdate ()
{
timer += Time.deltaTime;
// Store the input axes.
float h = CrossPlatformInputManager.GetAxisRaw("Horizontal");
float v = CrossPlatformInputManager.GetAxisRaw("Vertical");
// Move the player around the scene.
Move (h, v);
// Turn the player to face the mouse cursor.
Turning ();
// Animate the player.
Animating (h, v);
}
void Move (float h, float v)
{
// Set the movement vector based on the axis input.
movement.Set (h, 0f, v);
// Normalise the movement vector and make it proportional to the speed per second.
movement = movement.normalized * speed * Time.deltaTime;
// Move the player to it's current position plus the movement.
playerRigidbody.MovePosition (transform.position + movement);
}
void Update ()
{
if(Input.GetKeyUp(KeyCode.Space))
{
}
}
void OnCollisionEnter (Collision col)
{
if(col.gameObject.name == "coin10")
{
Destroy(col.gameObject);
CoinManager.coin=CoinManager.coin+10;
}
if(col.gameObject.name == "coin5")
{
Destroy(col.gameObject);
CoinManager.coin=CoinManager.coin+5;
}
if(col.gameObject.name == "coin1")
{
Destroy(col.gameObject);
CoinManager.coin=CoinManager.coin+1;
}
if(col.gameObject.name == "eterium1")
{
Destroy(col.gameObject);
EteriumManager.eterium=EteriumManager.eterium+1;
}
if(col.gameObject.name == "test")
{
}
}
void Turning ()
{
#if !MOBILE_INPUT
// Create a ray from the mouse cursor on screen in the direction of the camera.
Ray camRay = Camera.main.ScreenPointToRay (Input.mousePosition);
// Create a RaycastHit variable to store information about what was hit by the ray.
RaycastHit floorHit;
// Perform the raycast and if it hits something on the floor layer...
if(Physics.Raycast (camRay, out floorHit, camRayLength, floorMask))
{
// Create a vector from the player to the point on the floor the raycast from the mouse hit.
Vector3 playerToMouse = floorHit.point - transform.position;
// Ensure the vector is entirely along the floor plane.
playerToMouse.y = 0f;
// Create a quaternion (rotation) based on looking down the vector from the player to the mouse.
Quaternion newRotatation = Quaternion.LookRotation (playerToMouse);
// Set the player's rotation to this new rotation.
playerRigidbody.MoveRotation (newRotatation);
}
#else
Vector3 turnDir = new Vector3(CrossPlatformInputManager.GetAxisRaw("Mouse X") , 0f , CrossPlatformInputManager.GetAxisRaw("Mouse Y"));
if (turnDir != Vector3.zero)
{
// Create a vector from the player to the point on the floor the raycast from the mouse hit.
Vector3 playerToMouse = (transform.position + turnDir) - transform.position;
// Ensure the vector is entirely along the floor plane.
playerToMouse.y = 0f;
// Create a quaternion (rotation) based on looking down the vector from the player to the mouse.
Quaternion newRotatation = Quaternion.LookRotation(playerToMouse);
// Set the player's rotation to this new rotation.
playerRigidbody.MoveRotation(newRotatation);
}
#endif
}
void Animating (float h, float v)
{
// Create a boolean that is true if either of the input axes is non-zero.
bool walking = h != 0f || v != 0f;
// Tell the animator whether or not the player is walking.
anim.SetBool ("IsWalking", walking);
}
}
}
And this is the part where i need the timer start:
if(col.gameObject.name == "test")
{
}
I have try with timer -= time.deltatime but time.deltatime is not a timer. The value of the timer is the value of the object destroy.
Everyone can help me to do this timer?
Example:
if(timer<=10){
speed=10f;
}
else
{
speed=5f;
}
Use a Coroutine.
Start the Coroutine.
Change the player_speed.
Wait your time.
Reset the speed.
if(col.gameObject.name == "test")
{
StartCoroutine("SlowSpeed");
}
private IEnumerator SlowSpeed()
{
player_speed = 10;
yield return new WaitForSeconds(10f);
player_speed = 5;
}

Categories

Resources