Unity Shoot Ball From POV of Camera - c#

I know this has been asked a few times but I believe my question can be solved without too much trouble (hopefully!) and is somewhat unique. I'm writing a mini-golf script that shoots the ball, intended to shoot away from the POV of the camera. I can't get it to do so however. I'm sure it has something to do with camera.transform but not sure. I'm a total noob to coding in Unity. I just need a simple, straightforward way to get this dang golf ball to travel in a straight line in whatever direction the camera is facing. Please help!
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class HitBall2 : MonoBehaviour
{
Rigidbody rigidBody;
bool StartedShot;
Vector3 shotStart;
Vector3 shotEnd;
Vector3 direction;
public float distance;
public float forceAdjust = 0.05f;
void Start()
{
rigidBody = this.GetComponent<Rigidbody>();
StartedShot = false;
}
void Update()
{
if (Input.GetMouseButtonUp(1))
{
rigidBody.velocity = Vector3.zero;
this.transform.position = Vector3.zero;
StartedShot = false;
}
// Starting shot
if (!StartedShot && Input.GetMouseButtonDown(0))
{
StartedShot = true;
shotStart = Input.mousePosition;
}
// Ending shot
if (StartedShot && Input.GetMouseButtonUp(0))
{
shotEnd = Input.mousePosition;
direction = Camera.main.transform.forward - shotEnd;
float distance = direction.magnitude;
StartedShot = false;
Vector3 shootDirection = new Vector3(direction.x, 0.0f, direction.y);
rigidBody.AddForce(shootDirection * rigidBody.mass * forceAdjust, ForceMode.Impulse);
}
}
}

As mentined in comments above, to shoot forward just use cameras transform.forward

Okay I changed the script as follows and the ball now travels away from the camera. There's a little problem with the velocity of the ball and the little y-axis jump it does but I'm sure it's easily figured out.
changed " Vector3 shootDirection = new Vector3(direction.x, 0.0f, direction.y);"
to Vector3 shootDirection = Camera.main.transform.forward;
Thanks everyone!

Related

Camera relative moment

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.

How to face character in its movement direction and at the same time keep it aligned to any surface?

I want to move my player character(human) on a curved surface. But at the same time character shall stay perpendicular to the surface normals and it should face in the movement direction and can handle collisions(if there is a wall ahead, shall not be able to go through it).
I tried to make a parent stay over normals and change the child local rotation towards direction of motion of its parent. But it has several limitations as of now.
Here is the code what i was using:
[SerializeField] float raycastLength = 1f;
bool canPlayerMove = true;
public float speed = 2f;
public Vector3 offset; //object's position offset to ground / surface
public Quaternion childDirection;
private void Update()
{
float moveHorizontal = SimpleInput.GetAxis("Horizontal");
float moveVertical = SimpleInput.GetAxis("Vertical");
Ray ray = new Ray(transform.position, -transform.up);
RaycastHit hitInfo;
if (Physics.Raycast(ray, out hitInfo, raycastLength))
{
transform.rotation = Quaternion.LookRotation(Vector3.up, hitInfo.normal);
transform.position = hitInfo.point + offset;
Debug.DrawLine(ray.origin, hitInfo.point, Color.red);
}
if (canPlayerMove)
{
Vector3 movement = new Vector3(moveHorizontal, 0, moveVertical);
if (movement != Vector3.zero)
{
childDirection = Quaternion.Slerp(transform.GetChild(0).localRotation, Quaternion.LookRotation(movement), 0.15F);
transform.GetChild(0).localRotation = childDirection;
}
transform.Translate(movement * speed * Time.deltaTime, Space.Self);
}
}
first to not make your player go thru walls you want to add a collider to your walls and not set it as trigger, you will also need a rigidbody on your player and this will help in the next steps.
Secondly you will need to acces the rigidBody in code using this: (if you Check Use Gravity it will also stay on your terrain that you made)
private Rigidbody rb;
private float speed = 7.5f;
private void Start()
{
//this gets the rigidbody on the gameObject the script is currently on.
rb = this.GetComponent<Rigidbody>();
}
private void Update()
{
float hor = Input.GetAxis("Horizontal");
float vert = Input.GetAxis("Vertical");
//this will move your player frame independent.
rb.MovePosition(this.transform.position + new Vector3(hor, 0, vert) * speed *
Time.deltaTime);
}
Also make sure that you have a rigidBody on your player, else it will throw an error.

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;
}
}

Unity Rigidbody Click to move

I'm wanting to have a script that when I click in my scene, my player will rotate and have a force added to it and will travel until it has reached the clicked point in my scene.
Right now I have it working using Vectors and having my player lerp from one point to another. But I want to amend it so I can use physics and get a better sense of my player moving. Like have him speed up to start moving and slowing down as he reaches my target loction
My script now looks like this
public GameObject isActive;
public float speed;
public Ray ray;
public Rigidbody rigidBody;
public Vector3 targetPoint;
// Use this for initialization
void Start ()
{
targetPoint = transform.position;
}
// Update is called once per frame
void Update ()
{
}
void FixedUpdate()
{
if (Input.GetMouseButton(0))
{
targetPoint = Camera.main.ScreenToWorldPoint (Input.mousePosition);
ChangeRotationTarget ();
}
Quaternion targetRotation = Quaternion.LookRotation (targetPoint - transform.position);
transform.rotation = Quaternion.Slerp (transform.rotation, targetRotation, speed * Time.deltaTime);
rigidbody.position = Vector3.Lerp(transform.position, targetPoint, speed * Time.fixedDeltaTime);
}
void ChangeRotationTarget ()
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
Plane playerPlane = new Plane (Vector3.up, transform.position);
float hitdist = 0.0f;
if (playerPlane.Raycast (ray, out hitdist))
{
targetPoint = ray.GetPoint (hitdist);
}
}
However, when I run this, he just slides from one point to another. Regardless of the drag or mass I put in my rigidbody.
Can someone help me make my changes? Or point me in the right direction
I didn't had time to test this, but it should almost be what you are looking for. Just apply your Quaternion and Rotation codes to it.
void FixedUpdate() {
if (Input.GetMouseButton(0))
{
targetPoint = Camera.main.ScreenToWorldPoint(Input.mousePosition);
if (rigidBody.position != targetPoint)
{
reachedTargetPoint = false;
if (reachedTargetPoint == false)
{
GetComponent<Rigidbody>().AddForce(speed);
}
}
//zoneBeforeReachingTP should be how much units before reaching targetPoint you want to start to decrease your rigidbody velocity
if (rigidBody.position == (targetPoint - zoneBeforeReachingTP))
{
//speedReductionRate should be how much of speed you want to take off your rigidBody at the FixedUpdate time rate
rigidBody.velocity = speedReductionRate;
}
if(rigidBody.position == targetPoint)
{
rigidBody.velocity = new Vector3(0, 0, 0);
reachedTargetPoint = true;
}
ChangeRotationTarget();
}
}
That's because you modify the rigidbody.position, and your logic overrides the physics. What you have to do instead is to ApplyForce every physics frame, probably with ForceMode.Force or ForceMode.Acceleration.

Categories