Simple click-to-move script not working? - c#

Here is my code:
public class CharacterController : MonoBehaviour
{
private Vector3 _startLocation = Vector3.zero;
private Vector3 _currentLocation = Vector3.zero;
private Vector3 _endLocation = Vector3.zero;
private bool _isMoving = false;
private float _distanceToTravel;
private float _startTime;
public float Speed = 1.0f;
// Update is called once per frame
void Update()
{
if (Input.GetMouseButtonDown(0))
{
Debug.Log("Left mouse button clicked");
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit))
{
if (hit.collider.gameObject.CompareTag("Ground"))
{
_startLocation = transform.position;
_endLocation = hit.point;
_isMoving = true;
_startTime = Time.time;
_distanceToTravel = Vector3.Distance(_startLocation, _endLocation);
Debug.Log(string.Format("Ground has been hit: Start: {0}, End: {1}", _startLocation.ToString(), _endLocation.ToString()));
}
}
}
if (_isMoving)
Move();
}
void Move()
{
float timeElapsed = (Time.time - _startTime) * Speed;
float t = timeElapsed / _distanceToTravel;
_currentLocation = Vector3.Lerp(_startLocation, _endLocation, t);
transform.Translate(_currentLocation);
if (_currentLocation == _endLocation)
{
Debug.Log(string.Format("Destination reached ({0})", _endLocation.ToString()));
_isMoving = false;
}
}
}
I read the documentation on the Vector3.Lerp function, as well as the Physics.Raycast function, and ended up with this code.
The debug console confirms that the Ground has been hit, but my capsule starts moving upwards in the Y direction and never stops!
I'm still very new to Unity and game development in general, so I'm still learning, but any pointers on what I'm doing wrong?

it is moving in the Y because you use transform.Translate.
transform.Translate is moving it, so if you did transform.Translate(0, 0, 10)
it would move in the z, and if you did transform.Translate(0, 10, 10)
it will move in the y and z direction.
So to fix this i will show you 2 ways:
1) Using Vector3.Lerp:
transform.position = Vector3.Lerp(_startLocation, _endLocation, t);
2) Using MoveTowards:
transform.position = Vector3.MoveTowards(transform.position, _endLocation, Speed * Time.deltaTime);
In the first Vector3.Lerp is used, and i see you use it too
_currentLocation = Vector3.Lerp(_startLocation, _endLocation, t);
So you could do either this:
transform.position = Vector3.Lerp(_startLocation, _endLocation, t);
or this
transform.position = _currentLocation;
Both will do the same because you assigned _currentLocation to
Vector3.Lerp(_startLocation, _endLocation, t);
And you can read about MoveTowards here
http://docs.unity3d.com/ScriptReference/Vector3.MoveTowards.html

Check the Comments line You will understand what I had edited to bring the Solution.
It works well for me
void Start()
{
_startLocation = transform.position; // Changed Here to Initialize once
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
Debug.Log("Left mouse button clicked");
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit))
{
if (hit.collider.gameObject.CompareTag("Ground"))
{
print(hit.point);
//**Here you mentioned _StartLocation = transform.position
//this instantly changes the starting point Every time so
//if SP changes then totally changed in Translation.***
_endLocation= hit.point;
_isMoving = true;
_startTime = Time.time;
_distanceToTravel = Vector3.Distance(_startLocation, _endLocation);
}
}
}
if (_isMoving)
{
Move();
}
}
void Move()
{
float timeElapsed = (Time.time - _startTime) * Speed;
float t = timeElapsed / _distanceToTravel;
_currentLocation = Vector3.Lerp(_startLocation, _endLocation, t);
transform.position = Vector3.Lerp(_startLocation, _endLocation, t);
_startLocation = _endLocation;
//*** This line is for Next Mouse
//click. So every next click StartPoint is Current Click
//EndPoint***
if (_currentLocation == _endLocation)
{
Debug.Log(string.Format("Destination reached ({0})", _endLocation.ToString()));
_isMoving = false;
}
}
}

Related

Character rotation relative with camera forward?

I spent some time already and still cant figure it out by myself.
The problem is i can"t rotate character forward to move the way i pointing my camera.
To move camera i use cinemachine with Orbital Transposer and normal Composer.
My Movement script to let you know with what I work :
public class PlayerMovement : MonoBehaviour
{
public float m_Impulse = 1f;
public bool grounded = false;
public float groundCheckDistance;
private float bufferCheckDistance = 0.02f;
public float airMultiplier = 0.2f;
float horizontalInput;
float verticalInput;
public float groundDrag;
Rigidbody m_Rigidbody;
Vector3 moveDirection;
public float m_Speed = 5f;
public float m_runSpeed = 7f;
void Start()
{
m_Rigidbody = GetComponent<Rigidbody>();
m_Rigidbody.freezeRotation = true;
}
void FixedUpdate()
{
horizontalInput = Input.GetAxis("Horizontal");
verticalInput = Input.GetAxis("Vertical");
// calculate movement direction
moveDirection = orientation.forward * verticalInput + orientation.right * horizontalInput;
if (Input.GetKey(KeyCode.Space) & grounded)
{
m_Rigidbody.AddForce(Vector3.up * m_Impulse * 2);
}
if (Input.GetKey(KeyCode.LeftShift))
{
m_Rigidbody.AddForce(moveDirection * m_runSpeed, ForceMode.Force);
}
else
{
m_Rigidbody.AddForce(moveDirection * m_Speed, ForceMode.Force);
}
}
void Update()
{
groundCheckDistance = (GetComponent<CapsuleCollider>().height / 2) + bufferCheckDistance;
RaycastHit hit;
if (Physics.Raycast(transform.position, -transform.up, out hit, groundCheckDistance))
{
grounded = true;
}
else
{
grounded = false;
}
if (grounded)
{
m_Rigidbody.drag = groundDrag;
}
else if (!grounded)
{
moveDirection *= airMultiplier;
m_Rigidbody.drag = 2;
}
}
Hope you know how to make it works, Thank You in advance.
P.S. My camera script was left with Cursor.LockMode only cuz i messed something up rly bad and my camera was bugged.

Unity - how to make an object face the direction of its movement?

I'm trying to make a character prefab face the direction in which its moving. I've tired all sorts of things, with and without rigidbodies but nothing seems to work. The thing is, it does actually face in the correct direction. But once its there, it starts to rotate the whole prefab and it goes down into the ground.
The character holds a 3D shield, and goes towards a tower. So once it reaches the tower it raises the shield which in turn rotates the whole character down into the ground. I would like it to just rotate in the X and Z axis and never change the Y axis.
Here's my code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BlockRadar : MonoBehaviour
{
// Start is called before the first frame update
public Transform Tower;
private GameObject[] multipleBlocks;
public Transform closestBlock;
public bool blockContact;
public float currentDistance;
public float stopDistance;
public Animator animator;
public int damage;
public float attackspeed;
private float canAttack;
public float moveSpeed = 5f;
public Vector3 distance;
private Vector3 movement;
void Start()
{
closestBlock = null;
blockContact = false;
}
// Update is called once per frame
void Update()
{
Vector3 pos = transform.position;
pos.y = 0;
pos.x = 0;
transform.position = pos;
closestBlock = getClosestBlock();
closestBlock.gameObject.GetComponent<Renderer>().material.color = new Color(1, 0.7f, 0, 1);
Vector3 direction = closestBlock.position - transform.position;
direction.Normalize();
movement = direction;
float dist = Vector3.Distance(closestBlock.position, transform.position);
if (dist <= 1.5f)
{
{
blockContact = true;
animator.SetBool("Moving", false);
Debug.Log("Now touching block");
if (attackspeed <= canAttack)
{
Attack();
canAttack = 0f;
}
else
{
canAttack += Time.deltaTime;
}
}
}
if (dist > 1.5f)
{
transform.forward = movement;
blockContact = false;
Debug.Log("Lost contact with block");
animator.SetBool("Moving", true);
moveCharacter(movement);
}
}
public void Attack()
{
Debug.Log("ATTACKING!");
Damage(closestBlock.transform);
animator.SetTrigger("Attacking");
}
private void FixedUpdate()
{
}
void moveCharacter(Vector3 direction)
{
transform.Translate(direction * moveSpeed * Time.deltaTime, Space.World);
}
void DistanceToTower()
{
if (Tower)
{
float dist = Vector3.Distance(Tower.position, transform.position);
if (dist <= 1)
{
{
blockContact = true;
Debug.Log("Now touching block");
if (attackspeed <= canAttack)
{
Attack();
canAttack = 0f;
}
else
{
canAttack += Time.deltaTime;
}
}
}
}
}
//when the object carrying this script is destroyed
private void OnDestroy()
{
if (closestBlock !=null)
{
closestBlock.gameObject.GetComponent<Renderer>().material.color = new Color(0, 0, 0, 0);
}
}
public Transform getClosestBlock()
{
multipleBlocks = GameObject.FindGameObjectsWithTag("Block");
float closestDistance = Mathf.Infinity;
Transform trans = null;
//finds all blocks in the scene
foreach (GameObject go in multipleBlocks)
{
currentDistance = Vector3.Distance(transform.position, go.transform.position);
if (currentDistance < closestDistance)
{
closestDistance = currentDistance;
trans = go.transform;
}
}
return trans;
}
void Damage(Transform block)
{
Tower_Stats e = block.GetComponent<Tower_Stats>();
if (e != null)
{
e.TakeDamage(damage);
}
}
}
I would be really, really grateful for any help. As I said before I used to have rigidbodies on the character, but I removed them since I thought maybe they were the fault. But doesnt seem like it. One other thing I've noticed is that when the prefab is instantiated, its children doesnt have the correct position values. Not sure why. But if that could be a clue I just thought I'd let you know.

How can I move the object back to it's start position if the raycast is not hitting?

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class OnMouseOverEvent : MonoBehaviour
{
public float duration;
public string tag;
public Vector3 startPos;
public Vector3 endPos;
public float distancetoMove = 1f;
public float lerpTime = 5;
private float currentLerpTime = 0;
private void Start()
{
startPos = transform.position;
endPos = transform.position - Vector3.forward * distancetoMove;
}
private void Update()
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit, 100))
{
if (hit.transform.tag == tag)
{
currentLerpTime += Time.deltaTime;
if(currentLerpTime >= lerpTime)
{
currentLerpTime = lerpTime;
}
float Perc = currentLerpTime / lerpTime;
transform.position = Vector3.Lerp(startPos, endPos, Perc);
}
else
{
transform.position = Vector3.Lerp(endPos, startPos, Perc);
}
}
}
}
If it's hitting the object is moving smooth slowly forward but I want that while the raycast keep hitting the object it will move forward and if got to the distance keep it stay there as long as the raycast hitting it.
But once moving the mouse and the raycast is not hitting the object either in the middle of the movement or when it got to the distance move the object back to it's start position.
So when moving the mouse out of the object area it will start moving either forward to endPos or backward to startPos.
Another way to do it: (Without Raycast)
using UnityEngine;
public class Move : MonoBehaviour
{
public float speed = 5f;
public float distancetoMove = 1f;
public bool goForward;
public Vector3 startPos;
public Vector3 endPos;
private void Start()
{
startPos = transform.position;
endPos = transform.position - Vector3.forward * distancetoMove;
}
void Update()
{
if (goForward)
{
transform.position = Vector3.MoveTowards(transform.position, endPos, speed * Time.deltaTime);
}
else
{
transform.position = Vector3.MoveTowards(transform.position, startPos, speed * Time.deltaTime);
}
}
private void OnMouseOver()
{
goForward = true;
}
private void OnMouseExit()
{
goForward = false;
}
}
you have to declare Perc outsdide if if you want to use ot inside else
to solve your problem
change:
inside if (hit.transform.tag == tag)
transform.position = Vector3.Lerp(start, endPos, Perc);
to this:
transform.position = Vector3.Lerp(transform.position, endPos, Perc);
and inside else
else{
transform.position = Vector3.Lerp(endPos, startPos, Perc);
}
to this:
else{
transform.position = Vector3.Lerp(transform.position, startPos, Perc);
}
your code will become like this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class OnMouseOverEvent : MonoBehaviour
{
public float duration;
public string tag;
public Vector3 startPos;
public Vector3 endPos;
public float distancetoMove = 1f;
public float lerpTime = 5;
float Perc;
private float currentLerpTime = 0;
private void Start()
{
startPos = transform.position;
endPos = transform.position - Vector3.forward * distancetoMove;
}
private void Update()
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit, 100))
{
if (hit.transform.tag == tag)
{
currentLerpTime += Time.deltaTime;
if (currentLerpTime >= lerpTime)
{
currentLerpTime = lerpTime;
}
Perc = currentLerpTime / lerpTime;
transform.position = Vector3.Lerp(transform.position, endPos, Perc);
}
else
{
transform.position = Vector3.Lerp(transform.position, startPos, Perc);
}
}
}
}
Interpolant (Perc) not updating properly
Your object currently moves in the update by a Lerp, which the interpolant is Perc.
The problem is that your code for updating your Perc by lerp timer is only done when the raycast hits this object.
You can update your Perc by lerp-timer before/after you done the movement process, like so:
private float Perc;
private void Update()
{
GetPercByLerpTime(); // Update before it moves
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit, 100))
{
if (hit.transform.tag == tag)
{
transform.position = Vector3.Lerp(startPos, endPos, Perc);
}
else
{
transform.position = Vector3.Lerp(endPos, startPos, Perc);
}
}
// Or you can update your 'Perc' here.
}
private void UpdatePercByLerpTime(){
currentLerpTime += Time.deltaTime;
if(currentLerpTime >= lerpTime)
{
currentLerpTime = lerpTime;
}
Perc = currentLerpTime / lerpTime;
}
Note: You might notice that currentLerpTime will keep incrementing until it reaches lerpTime, and it will stay as the same value as lerpTime.
You might want to implement something that either decreases or resets currentLerpTime.
No movement occurs if raycast hits nothing.
Your currently code only moves the object if the raycast hits something. (Though it does move in the correct direction depending if it hit this object)
What you probably want is to move the object back to the start position if the raycast hit nothing or the raycast hit something else.
So your final result would look like this:
private float Perc;
private void Update() {
UpdatePercByLerpTime();
if (MouseRaycastHitThisObject()) {
transform.position = Vector3.Lerp(startPos, endPos, Perc);
} else {
transform.position = Vector3.Lerp(endPos, startPos, Perc);
}
// Or you can update your 'Perc' here.
}
// True if the raycastfrom the mouse position hits this object
private bool MouseRaycastHitThisObject(){
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit, 100)) {
return hit.transform.tag == tag;
}
// Raycast did not hit anything; Player's mouse is not hovering over anything.
return false;
}
private void UpdatePercByLerpTime(){
currentLerpTime += Time.deltaTime;
if(currentLerpTime >= lerpTime) {
currentLerpTime = lerpTime;
}
Perc = currentLerpTime / lerpTime;
}

Pushes the object on the axes

I have a problem that I can not solve. I have an object, and I want when I click somewhere near him to move with some force. something like the picture below.
I'd even want some idea!!!
Help!!!
public float speed;
public GameObject player;
Vector3 target;
private void Update()
{
if (Input.GetMouseButtonDown(0))
{
var mouseClick = Input.mousePosition;
mouseClick.z = player.transform.position.z - Camera.main.transform.position.z;
target = Camera.main.ScreenToWorldPoint(mouseClick);
var distanceToTarget = Vector3.Distance(player.transform.position, target);
if (distanceToTarget > 2)
{
target.z = 0;
player.GetComponent<Rigidbody2D>().AddForce(target * speed);
}
else
print("travelling COMPLETE");
}
}
Using the code you provided in your answer...
public float speed;
public GameObject player;
Vector3 directionVector;
private void Update()
{
if (Input.GetMouseButtonDown(0))
{
// Input.mousePosition is already in pixel coordinates so the z should already be 0.
Vector3 mouseWorldPosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
// Now that the mouse is a world position lets set his z to the same as the player
mouseWorldPosition.z = player.transform.position.z;
// Gives us a direction from the mouse to the player. So think of it as Player <- Mouse remove the < and you have the equation
directionVector = player.transform.position - mouseWorldPosition;
}
else
{
directionVector = Vector3.zero;
}
}
// Physics based movement should be done in FixedUpdate
private void FixedUpdate()
{
player.GetComponent<Rigidbody2D>().AddForce(directionVector.normalized * speed);
}

Moving object along a raycast in unity using c#

I want to move an object along a ray gradually,
I have the following code so far which moves it in 1 frame to the destination rather than smoothly gliding there.
If I remove the Destroy(projectile.rigidbody2D) line from the end it sort of does it but then bounces around all over the place?
Any help would be much appreciated!
void Update ()
{
if (Input.GetMouseButtonUp (1)) {
if (Hand.transform.childCount == 1){
projectile.gameObject.transform.parent = null;
Ray ray = new Ray(spawn.position,spawn.up);
RaycastHit hit;
float shotDistance = shotdistance;
if (Physics.Raycast(ray,out hit, shotDistance)) {
shotDistance = hit.distance;
}
projectile.AddComponent<Rigidbody2D>();
projectile.rigidbody2D.gravityScale = 0;
projectile.rigidbody2D.AddForce(Vector2.up * 5);
projectile.transform.position = Vector3.MoveTowards(projectile.transform.position,ray.direction * shotDistance,shotDistance);
Debug.DrawRay(ray.origin,ray.direction * shotDistance,Color.red,1);
Destroy(projectile.rigidbody2D);
}
}
}
private float lerpValue = 0;
public float speed;
private Vector3 startPosition;
private bool isMoving;
void Update ()
{
if (Input.GetMouseButtonUp (1))
{
if (Hand.transform.childCount == 1)
{
lerpValue = 0;
startPosition = projectile.transform.position;
projectile.gameObject.transform.parent = null;
Ray ray = new Ray(spawn.position,spawn.up);
RaycastHit hit;
float shotDistance = shotdistance;
if (Physics.Raycast(ray,out hit, shotDistance) && !isMoving)
{
isMoving = true;
shotDistance = hit.distance;
//speed = distance / time
//therefore: time = distance / speed
float time = shotDistance / speed;
StartCoroutine(ProjectileMotion(hit.point, time));
}
}
}
}
IEnumerator ProjectileMotion(Vector3 endPosition, float time)
{
while(lerpValue < time)
{
lerpValue += Time.deltaTime;
projectile.transform.position = Vector3.Lerp(startPosition, endPosition, lerpValue / time);
}
isMoving = false;
}

Categories