Camera relative moment - c#

I'm new to Unity 3D and I've started to study and learn player and camera mechanics these past few weeks.Although, I have a simple character controller system with a Cinemachine free look cam following the player, I need help incorporating a camera relative player movement mechanic into my project. I therefore need help incorporating such a mechanism into my system. I've added my PlayerController Code below
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
[SerializeField] public float _playerSpeed;
[SerializeField] public Rigidbody rb;
[SerializeField] private float _jumpforce = 100 ;
[SerializeField] private float fallMultiplier;
[SerializeField] private float lowJumpMultiplier;
public bool isGrounded;
public bool jumpReady;
public float jumpcoolDownTimer = 1.5f;
public float jumpcoolDownCurrent = 0.0f;
[SerializeField] private float sensi;
private float rotX;
private float rotY;
private Vector3 rotate;
int isJumpingHash;
int isFallingHash;
int isLandingHash;
public Animator animator;
Vector3 vel;
// Start is called before the first frame update
void Start()
{
jumpcoolDownCurrent = jumpcoolDownTimer;
Cursor.visible = false;
Cursor.lockState = CursorLockMode.Locked;
animator = GetComponent<Animator>();
//Converting string values to hash values to save memory
isJumpingHash = Animator.StringToHash("isJumping");
isFallingHash = Animator.StringToHash("isFalling");
isLandingHash = Animator.StringToHash("isGrounded");
var cam = Camera.main;
}
// Update is called once per frame
void Update()
{
Move();
Jump();
}
private void OnCollisionEnter(Collision collision){
if(collision.gameObject.tag == "Surface"){
isGrounded = true;
}
}
private void Move()
{
vel = new Vector3(Input.GetAxis("Horizontal"),0,Input.GetAxis("Vertical")) * _playerSpeed;
if(Input.GetKey(KeyCode.LeftShift) && Input.GetKey(KeyCode.W)){
transform.position += transform.forward * Time.deltaTime * _playerSpeed;
}
}
private void Jump(){
vel.y = rb.velocity.y;
rb.velocity = vel;
//Jump Cooldown timer
if(jumpcoolDownCurrent >= jumpcoolDownTimer){
jumpReady = true;
}
else{
jumpcoolDownCurrent += Time.deltaTime;
jumpReady = false;
}
bool jump = animator.GetBool(isJumpingHash);
bool fall = animator.GetBool(isFallingHash);
bool land = animator.GetBool(isLandingHash);
//Jump
if(Input.GetKeyDown(KeyCode.Space) && isGrounded && jumpReady){
rb.AddForce(Vector3.up * _jumpforce, ForceMode.Impulse);
isGrounded = false;
jumpcoolDownCurrent = 0.0f;
animator.SetBool(isJumpingHash, true);
jump = true;
}
//Fall
if((rb.velocity.y <= 0 && !isGrounded)){
rb.velocity += Vector3.up * Physics.gravity.y * (fallMultiplier - 1) * Time.deltaTime;
animator.SetBool(isFallingHash, true);
fall = true;
}
//Land
if(isGrounded && fall){
fall = false;
animator.SetBool(isFallingHash, false);
animator.SetBool(isLandingHash, true);
}
//Back to 2d movement
if(isGrounded && rb.velocity.y <= 0){
animator.SetBool(isLandingHash, false);
land = false;
animator.SetBool(isJumpingHash, false);
jump = false;
animator.SetBool(isFallingHash, false);
fall = false;
}
}
}
I've referred to several different YouTube tutorials and and also surfed different forums to find a solution, but to no avail. But I did notice that all these users were using the Quaternion Rotation mechanics and cam transforms. Upon attempting to incorporate these codes into my project, the camera continuously rotates as I try to rotate the camera in one particular direction, or the A and D (strafe left and right) animations weren't functioning properly.
P.S -> For those of you who don't know, camera relative moment is the mechanic that most third person games use now, where the player while in movement, turns along the rotation and the camera and runs in the direction the camera is facing. Good examples of that would be God of War (2018), Batman Arkham Series etc.

I highly recommend that you don't use CineMachine, I have never seen anyone able to use it successfully and it is very simple to make your own camera controller after you study for a few days (or pay for someone to make one for you).
To make Camera Relative Movement in a platformer game similar to God of War, you should look into transform.Rotation, EulerAngles and Quaternions (Penny De Byl has good online classes and books on dealing with Quaternions, they're Vector4 and very difficult to wrap your head around)
Then, throughout your game, add a bunch of walls with a trigger collider attached that rotate the camera to 1 angle or another when the player crosses them.
You might even want to make the camera a child of the player object then it will always have the perfect angle and move when the player rotates.
If you ask a more specific question later, then a more specific answer can be given.

Using Camera.main.transform.forward you can get the forward direction relative to the camera. You can use Camera.main.transform.right to get the right direction. If you set the y coordinate of these to zero, you can use these to define the direction you travel in. You would then need to normalize it, and multiply it by your input.
So, here is what I would do:
get forward direction for camera
scale it so that the vertical component is zero
normalize it so that you get a normalized directional vector
multiply by your vertical movement to get your vector for movement forward and back
do the same for right and left movement
add the 2 parts together
multiply by speed to get your velocity
Like this:
vel = (Vector3.Scale(Camera.main.transform.forward, new Vector3(1, 0, 1)).normalized * Input.GetAxis("Vertical"))
+ (Vector3.Scale(Camera.main.transform.right, new Vector3(1, 0, 1)).normalized * Input.GetAxis("Horizontal")))
* playerSpeed;
Also, while it is not relevant to your issue at the moment, it is best to normalize your input vector. So do something like: Vector3 inputDirection = new Vector3(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical"), 0f).normalized. Otherwise, diagonal movement ends up being much faster than movement in one direction.

Related

Multiple touch unity mobile

I am creating a 2d mobile game where one of the scripts uses a joystick to move and the other script lets the player shoot an object when tapping anywhere on the screen. The issue is when using the joystick it also shoots at the same time in that direction. Is there a way to separate the touches so when you use the joystick it does not immediately shoot to that direction but the player can still move and shoot anywhere at the same time?
Move Code
private void Update()
{
Vector2 moveInput = new Vector2(joystick.Horizontal, joystick.Vertical);
moveAmount = moveInput.normalized * speed;
}
Shoot code
private void Update()
{
Vector2 direction = Camera.main.ScreenToWorldPoint(Input.mousePosition) - transform.position;
float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;
Quaternion rotation = Quaternion.AngleAxis(angle - 90, Vector3.forward);
transform.rotation = rotation;
if(Input.GetMouseButton(0))
{
if (Time.time >= shotTime)
{
Instantiate(projectile, shotPoint.position, transform.rotation);
shotTime = Time.time + timeBetweenShots;
}
}
}
Instead of using Input.mousePosition you'll have to use Input.GetTouch. You can loop through it using Input.touchCount to find the first touch that is not interacting with a ui element, than use that touch instead of Input.mousePosition to find the direction to shoot (or not shoot if there is no touch). To find out if a specific touch is over ui you need a reference to the scene's EventSystem (or use EventSystem.current), and use EventSystem.IsPointerOverGameObject with Touch.fingerId.
If the joystick is not a ui element you'll need a different way to detect if the touch is over the joystick. For example you could check the pixel position, or see if the joystick itself has an "interacting fingerId". But with the assumption that the joystick is an ui element, here's one way to do what I wrote above: (untested)
private void Update()
{
var eventSystem = EventSystem.current;
for (var i = 0; i<Input.touchCount; i++)
{
var touch = Input.GetTouch(i);
if (eventSystem.IsPointerOverGameObject(touch.fingerId))
{
continue;
}
ShootToScreenPos(Vector2 screenPos);
break;
}
}
private void ShootToScreenPos(Vector2 screenPos)
{
Vector2 direction = Camera.main.ScreenToWorldPoint(screenPos) - transform.position;
float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;
Quaternion rotation = Quaternion.AngleAxis(angle - 90, Vector3.forward);
transform.rotation = rotation;
if (Time.time >= shotTime)
{
Instantiate(projectile, shotPoint.position, transform.rotation);
shotTime = Time.time + timeBetweenShots;
}
}

How to make a smooth crosshair in Unity3D?

In Unity 3D I'd like to create a crosshair for my top-down 2D-shooter that gradually moves to its target whenever the player has the same x-position as the target.
The problem is that I want a smooth animation when the crosshair moves to the target. I have included a small gif from another game that shows a crosshair I'd like to achieve. Have a look at it:
Crosshair video
I tried to do that with the following script but failed - the crosshair jumps forth and back when the enemies appear. It doesn't look so smooth like in the video I mentioned above.
The following script is attached to the player:
[SerializeField]
private GameObject crosshairGO;
[SerializeField]
private float speedCrosshair = 100.0f;
private Rigidbody2D crosshairRB;
private bool crosshairBegin = true;
void Start () {
crosshairRB = crosshairGO.GetComponent<Rigidbody2D>();
crosshairBegin = true;
}
void FixedUpdate() {
//Cast a ray straight up from the player
float _size = 12f;
Vector2 _direction = this.transform.up;
RaycastHit2D _hit = Physics2D.Raycast(this.transform.position, _direction, _size);
if (_hit.collider != null && _hit.collider.tag == "EnemyShipTag") {
// We touched something!
Debug.Log("we touched the enemy");
Vector2 _direction2 = (_hit.collider.gameObject.transform.position - crosshairGO.transform.position).normalized;
crosshairRB.velocity = new Vector2(this.transform.position.x, _direction2.y * speedCrosshair);
crosshairBegin = false;
} else {
// Nothing hit
Debug.Log("nothing hit");
crosshairRB.velocity = Vector2.zero;
Vector2 _pos2 = new Vector2(this.transform.position.x, 4.5f);
if (crosshairBegin) crosshairGO.transform.position = _pos2;
}
}
I think you need create a new variable call Speed translation
with
speed = distance from cross hair to enemy position / time (here is Time.fixedDeltaTime);
then multiply speed with velocity, the cross hair will move to enmey positsion in one frame.
but you can adjust speed by mitiply it with some float > 0 and < 1;

Can't rotate and move at the same time

I'm currently developing an FPS shooter in Unity 3D. I'm fairly new to Unity and I've been having a bit of trouble with my player movement script. Individually everything seems to work, I can rotate and move the player freely, however when I try doing the two simultaneously my player seems to lock and won't rotate.
Sometimes jumping or moving to higher ground on the terrain seems to fix the issue, however I ran a few checks with gravity disabled, no colliders, and with the player well above ground, so the problem seems to be with the script. I've also done a bit of debugging and the Rotate() code does run, just the rotation amount doesn't seem to change.
Here is the player movement script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(Rigidbody))]
public class PlayerMov : MonoBehaviour
{
[SerializeField]
private Camera cam;
[SerializeField]
private float speed = 5f;
[SerializeField]
private float looksensitivity = 4f;
[Header("Camera View Lock:")]
[SerializeField] //min and max amount for looking up and down (degrees)
private float lowlock = 70f;
[SerializeField]
private float highlock = 85f;
private Rigidbody rb;
private float currentrotx; //used later for calculating relative rotation
public Vector3 velocity;
public Vector3 rotation; // rotates the player from side to side
public float camrotation; // rotates the camera up and down
public float jmpspe = 2000f;
void Start()
{
rb = GetComponent<Rigidbody>();
}
// Update is called once per frame
void Update()
{
CalcMovement();
CalcRotation();
Rotate();
}
void FixedUpdate()
{
Move();
}
private void CalcRotation()
{
float yrot = Input.GetAxisRaw("Mouse X"); //around x axis
rotation = new Vector3(0f, yrot, 0f) * looksensitivity;
float xrot = Input.GetAxisRaw("Mouse Y"); //around y axis
camrotation = xrot * looksensitivity;
}
private void CalcMovement()
{
float xmov = Input.GetAxisRaw("Horizontal");
float zmov = Input.GetAxisRaw("Vertical");
Vector3 movhor = transform.right * xmov;
Vector3 movver = transform.forward * zmov;
velocity = (movhor + movver).normalized * speed;
}
void Move()
{
//move
if (velocity != Vector3.zero)
{
rb.MovePosition(rb.position + velocity * Time.fixedDeltaTime);
}
//jump
if (Input.GetKeyDown(KeyCode.Space))
{
//add double jump limit later!
rb.AddForce(0, jmpspe * Time.deltaTime, 0, ForceMode.Impulse);
}
}
void Rotate()
{
//looking side to side
rb.MoveRotation(rb.rotation * Quaternion.Euler(rotation));
// camera looking up and down
currentrotx -= camrotation;
currentrotx = Mathf.Clamp(currentrotx, -lowlock, highlock);
cam.transform.localEulerAngles = new Vector3(currentrotx, 0, 0);
}
}
Here are the relevant components attached to my player:
(The player has a couple more components attached but I ran tests without them and the problem still occurs)
Like I said I'm a bit of a unity novice, so i'm sure I missed something small but I just can't seem to place my finger on it, I've been stuck on this for a while so any help is much appreciated.
SOLVED:
It seems the problem I had was because I was running the scene from my laptop and not a desktop which I assume is what the Unity input was built for.

Unity 2D, C# - Creating an AI that both follows the player and flutters around him

First of all, I am EXTREMELY new to coding in general, but I've picked it up as a hobby, so please forgive my general lack of understanding.
I am putting together a sidescrolling platformer, in which a small character follows you around and gives you advice, lights the way, fills in lore, etc. Think Navi from Legend of Zelda. I already have a functioning script for following the player around, which I will post below.
However, I cannot get it to then lazily float around the character rather than just sitting still.
public class WillOWispFollowPlayer : MonoBehaviour
{
public Transform target;
public float smoothSpeed = 0.05f;
public Vector3 offset;
private void FixedUpdate()
{
Vector3 desiredPosition = target.position + offset;
Vector3 smoothedPosition = Vector3.Lerp(transform.position, desiredPosition, smoothSpeed);
transform.position = smoothedPosition;
}
}
I haven't dabbled to far into game development, but how I would go about it is to implement some random location with a bias towards the player or a target position near the player.
So, say that at semi even intervals, you generate a new random position or direction for your companion to move towards to, the distance should be relatively close to the companions current location. To prevent your companion randomly flying off, into the sunset, you should also make sure that this random location or direction is biased towards the players location, for example, the further away it is from the player, the more biased it will be to move towards the player.
After hammering away at this for a while, and looking at some other people's code, this is what I've come up with. It adds two functions to the original code. One is a random direction function with adjustable acceleration and deceleration which is applied in FixedUpdate. The other is a short burst of velocity in a separate random direction. The effect is more fluttery than I originally intended, and less of a floaty wander effect, but it works. If anyone has any ideas to make this a less jittery and more smooth, floaty effect, Im open to suggestions.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(Rigidbody2D))]
public class WillOWispFollowPlayer : MonoBehaviour
{
public Transform target;
public float smoothSpeed = 0.05f;
public float smoothSpeedSlow = 0.02f;
public Vector3 offset;
public float hoverSpeed = 10000f;
public Rigidbody2D rb;
public static System.Timers.Timer hoverTimer;
public float acceleration;
public float deceleration;
private Rigidbody2D physics;
private Vector2 direction;
private void Start()
{
rb = GetComponent<Rigidbody2D>();
InvokeRepeating("HoverDirection", 0, 1); //calling the DoverDirection every second
physics = GetComponent<Rigidbody2D>();
physics.gravityScale = 0;
physics.drag = deceleration;
}
private void FixedUpdate()
{
Vector3 desiredPosition = target.position + offset;
Vector3 smoothedPosition = Vector3.Lerp(transform.position, desiredPosition, smoothSpeed);
Vector3 smoothedPositionSlow = Vector3.Lerp(transform.position, desiredPosition, smoothSpeedSlow);
//setting up a "wander zone" where the Will'o'the'Wisp is less restricted within a defined distance of the player
if (Vector3.Distance((target.position + offset), this.transform.position) >= .5)
{
transform.position = smoothedPosition;
}
else
{
transform.position = smoothedPositionSlow;
}
//Random direction generator This results in a "flutter" effect
direction = UnityEngine.Random.insideUnitCircle;
Vector2 directionalForce = direction * acceleration;
physics.AddForce(directionalForce * Time.deltaTime, ForceMode2D.Impulse);
}
//A regular and prominant directional change, resulting in short darting motions around the target position
void HoverDirection()
{
rb.velocity = Random.onUnitSphere * hoverSpeed;
}
}

Character won't jump in Unity2D but entered the jump statement

I have a little problem with my player control script (C#) in the unity enigne. I worked out the following script with the basic movement of the player. The problem is that the player can enter the jump statement (the debug log printed it out)
Debug Log
but it will not work. The character is still on the ground.
The jump function will be enabled when the player is on the ground (grounded) and did not a double jump.
So my question is are there any "code mistakes" or maybe some configuration problems which I do not see?
Thank you for your help in advance!
using UnityEngine;
using System.Collections;
public class PlayerControl : MonoBehaviour
{
// public variables
public float speed = 3f;
public float jumpHeight = 5f;
// private variables
Vector3 movement;
Animator anim;
Rigidbody2D playerRigidbody;
// variables for the ground check
public Transform groundCheck;
public float groundCheckRadius;
public LayerMask whatIsGround;
private bool grounded;
private bool doubleJump;
// Use this for initialization
void Start()
{
}
// Update is called once per frame
void Update()
{
// Proves if the player is on the ground and activate the double jump function
if (grounded)
{
doubleJump = false;
}
// First line of proving the jump
if (Input.GetMouseButtonDown(0) && grounded)
{
Debug.Log("Jump if entered");
Jump();
}
if (Input.GetMouseButtonDown(0) && !doubleJump && !grounded)
{
Debug.Log("double Jump");
Jump();
doubleJump = true;
}
// Flipping the Player when he runs back
if (Input.GetAxis("Horizontal") < 0)
{
playerRigidbody.transform.localScale = new Vector2(-1.7f, 1.7f);
}
else
{
playerRigidbody.transform.localScale = new Vector2(1.7f, 1.7f);
}
}
void Awake()
{
// References setting up
playerRigidbody = this.GetComponent<Rigidbody2D>();
anim = GetComponent<Animator>();
}
void FixedUpdate()
{
float horizontal = Input.GetAxisRaw("Horizontal");
float vertical = Input.GetAxisRaw("Vertical");
// simple Movement without a speed control
Move(horizontal, vertical);
Animating(horizontal, vertical);
// Section for ground detection
grounded = Physics2D.OverlapCircle(groundCheck.position, groundCheckRadius, whatIsGround);
// Set the parameter for the jump animation false or true
anim.SetBool("Grounded", grounded);
}
void Move(float horizontal, float vertical)
{
movement.Set(horizontal, 0f, vertical);
movement = movement.normalized * speed * Time.deltaTime;
playerRigidbody.MovePosition(transform.position + movement);
}
void Jump()
{
playerRigidbody.AddForce(Vector3.up * jumpHeight);
// playerRigidbody.AddForce(new Vector2(0f, jumpHeight), ForceMode2D.Impulse);
Debug.Log("Jump function");
}
void Animating(float h, float v)
{
bool walking = h != 0f || v != 0f;
anim.SetBool("IsWalking", walking);
}
}
Just guessing here, but maybe Vector3.up does not work for 2D physics? I'm not really into 2D, but you could try
playerRigidbody.AddForce(transform.up * jumpHeight);
instead.
Also, have you tried different values for jumpHeight? 5 might be way to small depending on the mass you set for your rigidbody.
And make sure you haven't restricted any axes in the inspector.
// Note: If you want the object to move in a reliable predictable way but still allow physics interactions, use MovePosition (set the object to kinematic if you want it to be unaffected by physics but still be able to affect other things, and uncheck kinematic if you want both objects to be able to be acted on by physics.
If you want to move your object but let physics handle the finer details, add a force.
playerRigidbody.rigidbody2D.AddForce(Vector3.up * 10 * Time.deltaTime);
use Vector.up, Vector.down, vector.right, Vectore.left along with time.deltaTime to have smooth movement along the frame.

Categories