Making Player dash towards Cursor with a set length - c#

I'm trying to add dashing to the player in a Diablo-esque game I'm working on, and it "works" where you can dash towards your cursor.
if (isDashing)
{
Ray ray = cam.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
if (Physics.Raycast(ray, out hit, 100, movementMask))
{
motor.MoveToPoint(hit.point);
motor.speed = 150;
}
}
I get the mouse position, then move to that position with enormous velocity. MoveToPoint() function uses NavMeshAgent's SetDestination(point) function and this again "works" but the problem is naturally there isn't a limit to how far you can dash. You can have the mouse way over at the end of the screen and the character will still dash there.
I figured I should be using a Vector3 to define the limits of the dash, but I can't quite nail it down. I get the direction below and then apply the magnitude, but it does not work how I imagined it would.
if (Physics.Raycast(ray, out hit, 100, movementMask))
{
float mag = (hit.point - transform.position).magnitude;
if (mag < dashLength)
{
Vector3 dir = (hit.point - transform.position).normalized;
Vector3 newVector = dir * (dashLength - mag);
motor.MoveToPoint(newVector);
}
else
motor.MoveToPoint(hit.point);
motor.agent.speed = 150;
}
Any help would be appreciated

The main issue here is that you are treating a movement vector like a position. You want to start a the current transform.position and ADD the movement vector you calculated.
Further instead of extracting and checking the magnitude you can simply use Vector3.ClampMagnitude to keep the direction but make sure that the magnitude is not greater than your dashLength like e.g.
if (Physics.Raycast(ray, out hit, 100, movementMask))
{
// Calculate the difference only once
var movement = hit.point - transform.position;
// Make sure that delta has a maximum magnitude of dashLength
movement = Vector3.ClampMagnitude(delta, dashLength);
// MoveToPoint expects a position not only the movement vector
// so you start at your current position and add the movement
var newPosition = transform.position + movement;
motor.MoveToPoint(newPosition);
motor.agent.speed = 150;
}

Related

How to find direction and calculate end point of a raycast Unity2D

I am trying to make a gun in Unity that shoots a Raycast with a Line. (in 2d) I am using the player position as the origin and the mouse position as the direction. This works perfectly when there is a collision detection, but when there is not a collision, I have to find the direction and calculate the end point manually, because I still want the line to render if the player misses their shot. Here is my code to find this end point:
//finding the position of Mouse in worldspace
Vector3 mousePos = CameraController.mouseToWorld(Input.mousePosition);
mousePos.z = 0;
//Calculating whether or not the raycast is a hit
RaycastHit2D Ray = Physics2D.Raycast(Player.transform.position,mousePos,distance);
if(Ray.collider != null)
{
PassTheHit(Ray, Ray.collider.gameObject.name);
point = Ray.point;
}
else
{
//Calculating the farthest point on the line. this does not work.
point = (transform.position - mousePos).normalized;
point *= -1 * (distance / 2);
point.z = 0f;
}
Particularly note the else{} statement. The calculation to find the point does not work. It is waaaay too long and does not shoot in the right direction.
Any help is greatly appreciated.
Either way you are using your Physics2D.Raycast wrong. It expects a
Start position
and a direction
you are passing in a second position.
What you rather want to do is using the direction
Vector2 mousePos = CameraController.mouseToWorld(Input.mousePosition);
Vector2 direction = ((Vector2)(mousePos - transform.position)).normalized;
which then you can use for both things
RaycastHit2D Ray = Physics2D.Raycast(Player.transform.position, /*here*/ direction, distance);
if(Ray.collider != null)
{
PassTheHit(Ray, Ray.collider.gameObject.name);
point = Ray.point;
}
else
{
point = transform.position + /*and here*/ direction * distance;
}
Sidenote: Avoid to give a RaycastHit2D the name Ray as there is a type that is called Ray so it is misleading ;)

Moving an object in all directions utilizing a mouse in 3D-space

I have this tracking project that I have worked on for a long time. Basically, the User moves a cube to trace the movements of a sphere which moves randomly. Currently, I am working with the User movements.
The issue is that the device that the user utilizes to move in all directions is a mouse in combination with the arrow keys. This is because the mouse handles two dimensions (x-y), while the arrow keys handle two dimensions (x-z). However, I would like to make it possible for me to just use the mouse.
As such, my professor suggested that I use the shift key to switch between x-y and x-z movements. However, I am confused as to how to go about this.
The code below represents what I have at this moment and what I have tried in regards to the shift key movements.
Could someone please help me solve this issue, or is there a better way to go about this?
Thank you!
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(MeshCollider))]
public class UserController : MonoBehaviour {
public int speed = 20;
// Use this for initialization
void Start()
{
}
// Update is called once per frame
void Update()
{
// get input data from keyboard or controller
float moveHorizontal = Input.GetAxis("Horizontal");
float moveVertical = Input.GetAxis("Vertical");
// update player position based on input
Vector3 position = transform.position;
position.x += moveHorizontal * speed * Time.deltaTime;
position.z += moveVertical * speed * Time.deltaTime;
transform.position = position;
}
void OnMouseDrag()
{
while(Input.GetMouseButton(0))
{
int shiftCalled = 0;
//3D Drag, courtesy of Unity Forums
if(Input.GetKey(KeyCode.LeftShift))
{
float distance_to_screen = Camera.main.WorldToScreenPoint(gameObject.transform.position).z;
transform.position = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, distance_to_screen));
}
//Plane Drag, courtesy of Unity Forums
if(Input.GetKey(KeyCode.RightShift))
{
float distance_to_screen = Camera.main.WorldToScreenPoint(gameObject.transform.position).z;
Vector3 pos_move = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, distance_to_screen));
transform.position = new Vector3(pos_move.x, transform.position.y, pos_move.z);
}
}
}
}
Here's what I would do -
Imagine a ray going from the position of the mouse on your screen towards a 2D plane that stretches on the X and Z angles. The intersection of said ray with the plane would be the X,Z coordinated that you wish your object to be dragged on.
Now imagine the same ray intersecting with a different plane that stretches on the X and Y coordinates. This will generate the other type of movement you are looking for.
By holding the SHIFT button you will essentially be swapping these two planes, which should generate the result you are looking for.
You can generate both planes in code using -
xy_Plane = new Plane(Vector3.forward, m_DistanceFromCamera);
xz_Plane = new Plane(Vector3.up, m_DistanceFromCamera);
Generate the rays using -
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
//Initialise the enter variable
float enter = 0.0f;
if (xy_Plane.Raycast(ray, out enter))
Vector3 hitPoint = ray.GetPoint(enter);
and check which plane should be used with -
if(Input.GetKey(KeyCode.Shift))
Now you only need to move your object based on the hitPoint you received.
Hope this helps.

Transforming positions and adding a constant z axis valueC#

I have just started working on another project where one of the game mechanics is the ability to relocate a drone to the position of your mouse cursor.
Here is my code below where I have several problems(will specify)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class lightDrone : MonoBehaviour
{
Vector3 newPosition;
void Start()
{
newPosition = transform.position + (0,0,10); //***Problem A***
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
RaycastHit hit;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit))
{
newPosition = hit.point;
transform.position = newPosition; // ***Problem B***
}
}
}
}
Problem A - I am trying to have the new position = wherever the cursorhas clicked + 10 on the y axis so the "Drone" is seemed to be flying and not in the ground. This isn't achieving anything and is just giving me compiling errors.
I want the exact position to be (cursor.x, (public int (y)), cursor.z) but I have a very vague idea to do it.
Problem B -
Currently when I click my mouse, the object moves to the cursor but it seems to be just teleporting instantly. I want it to move at a certain speed and I think I need a public float to do so and change transform.position to translate.position but this again doesn't work.
Thank you in advance for answering my questions, I am trying to learn these new mechanics and how to code them. :)
Problem A
If you want your new position to be where ever the cursor clicked + 10 on the y axis it makes no sense to put any of your code in Start(). That's for initialization only. It runs just once at the beginning of the scene. Just add the 10 to your newPosition in the Update() method.
Problem B
Setting transform.position to a certain value will make your transform instantly move to that position. If you want to do it over time you need to move your transform a few increments at a time. Since this introduces that "over time" element, you need to use a method that takes place over time, so you need coroutines, asyncs or you can use just the update method if you're a tiny bit crafty.
Also you need the ray to intersect and hit something. Like a flat plane, the ground, terrain, anything with a collider. If it doesn't hit anywhere then you won't have a new place to move to.
Code
using UnityEngine;
public class LightDrone : MonoBehaviour
{
public float speed = 60.0f;
// Our destination needs to be remembered outside a single iteration of
// Update. So we put it outside of the method in order to remember it
// across multiple frames.
private Vector3 currentDestination;
// We need to check if we're at the destination yet so we know when to stop.
private bool notAtDestinationYet;
// When we're closer to the destination than this tolerance, we decide that
// we have arrived there.
private float tolerance = 0.1f;
private void Update()
{
if (Input.GetMouseButtonDown(0))
{
RaycastHit hit;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit))
{
var newPosition = hit.point;
// You can add whatever height you want here. I added
// just 2 instead of 10.
currentDestination = newPosition + new Vector3(0, 2.0f, 0);
notAtDestinationYet = true;
}
}
if (notAtDestinationYet)
{
// Use a bit of vector math to get the direction from our current
// position to the destination. The direction is a normalized Vector
// that we can just multiply with our speed to go in that direction
// at that specific speed!
var heading = currentDestination - transform.position;
var distance = heading.magnitude;
var direction = heading / distance;
// Check if we've arrived at our destination.
if (distance < tolerance)
{
notAtDestinationYet = false;
}
else
{
// If the remaining distance between us and the destination is
// smaller than our speed, then we'll go further than necessary!
// This is called overshoot. So we need to make our speed
// smaller than the remaining distance.
// We also multiply by deltaTime to account for the variable
// amount of time that has passed since the last Update() call.
// Without multiplying with the amount of time that has passed
// our object's speed will increase or decrease when the
// framerate changes! We really don't want that.
float currentSpeed = Mathf.Clamp(speed * Time.deltaTime,
Mathf.Epsilon, distance);
transform.position += direction * currentSpeed;
}
}
}
}

Applying force to sphere sends it in unpredictable directions

I am trying to move (fire) a sphere in the direction of a mouse click. But when I click, the sphere moves in unpredictable directions.
My code for adding the force to move the sphere:
if (Input.GetMouseButtonDown(0)){
RaycastHit hit;
/**
* We r using raycasting to detect mouse click on plane
* */
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit)){
newPosition = hit.point;
this.GetComponent<Rigidbody>().AddForce( (newPosition).normalized * 25, ForceMode.Impulse );
}
}
You've supplied an incorrect argument for Rigidbody.AddForce(). The vector you supply for the first argument should not be the position you want the force to point towards - it should be the direction of the force, multiplied by the magnitude.
In this case, you can calculate the direction of the force by subtracting the current position of the object from the target position:
Vector3 forceDir = newPosition - transform.position;
this.GetComponent<Rigidbody>().AddForce( forceDir.normalized * 25, ForceMode.Impulse );
Hope this helps! Let me know if you have any questions.

Moving perpendicular to an object

im trying to move between a path made from different objects. I apply a constant speed with Translate() and Rotating with the perpendicular vector from the object on the right using Raycasting
Although it turns , is it does not rotate fast enough to fully turn and moves out of the path.
Any ideas how to fix this? Or some other way to implement it?
any help will be appreciated
image to help visualize:
Raycast and Rotation image
void Update()
{
RaycastHit hit;
if (!Physics.Raycast(transform.position, Vector3.right, out hit))
return;
MeshCollider meshCollider = hit.collider as MeshCollider;
if (meshCollider == null || meshCollider.sharedMesh == null)
return;
Mesh mesh = meshCollider.sharedMesh;
Vector3[] normals = mesh.normals;
int[] triangles = mesh.triangles;
Vector3 n0 = normals[triangles[hit.triangleIndex * 3 + 0]];
Vector3 n1 = normals[triangles[hit.triangleIndex * 3 + 1]];
Vector3 n2 = normals[triangles[hit.triangleIndex * 3 + 2]];
Vector3 baryCenter = hit.barycentricCoordinate;
Vector3 interpolatedNormal = n0 * baryCenter.x + n1 * baryCenter.y + n2 * baryCenter.z;
interpolatedNormal = interpolatedNormal.normalized;
Transform hitTransform = hit.collider.transform;
interpolatedNormal = hitTransform.TransformDirection(interpolatedNormal);
Vector3 targetDir = Vector3.Cross(interpolatedNormal, Vector3.up); // Get the perpendicular vector
Vector3 newDir = Vector3.RotateTowards(transform.forward, targetDir, 20f, 0f);
transform.rotation = Quaternion.LookRotation(newDir); // Rotate Object
transform.Translate(0,0,0.2f); // Constant Speed
Debug.DrawRay(transform.position, perp,Color.red);
}
I don't think this is a good method, but it works for me. Maybe this can help you.
public float fixedDist = 2.0f;
void WallDetect() {
RaycastHit hit;
if (!Physics.Raycast(transform.position, transform.TransformPoint(Vector3.right) - transform.position, out hit))
return;
Vector3 perp = Vector3.Cross(hit.normal, Vector3.up);
Vector3 targetDir = Vector3.Project(transform.forward, perp).normalized;
Vector3 currentDir = transform.TransformPoint (Vector3.forward) - transform.position;
RaycastHit hit2;
if (Physics.Raycast (transform.position, -hit.normal, out hit2)) {
Vector3 fixedPos = hit2.point + hit.normal * fixedDist;
Vector3 predictPos = fixedPos + targetDir;
transform.position = Vector3.MoveTowards (transform.position, predictPos, 0.01f);
transform.rotation = Quaternion.Lerp(transform.rotation, Quaternion.LookRotation (predictPos - transform.position), 0.05f);
}
}
First thing: you seem not to be using data from HitInfo struct - and believe it or not it already contains a .normal Vector3 member, calculated during the raycast (or lazily calculated when requested, I am not sure but it makes no difference), it's best to use it rather than roll your own, simpler and less error prone (your manual finding of the normal looks correct neverless, I haven't tried it though)
Second thing: your last line has a Quaternion.Lerp with t=0.05 which means for each new rotation, you are still taking 95% of the original rotation, which is pretty darn slow rotation indeed. Try something in the range of Time.deltaTime (which is rougly an equivalent of getting close within a second)
Third thing: for rotation its better to use Slerp rather than Lerp, unless you are realt concerned about performance, which doesn't seem to be an issue considering the rest of the code.
Fourt thing: instead of hardcoding linear and rotation speed, try to use multiplies of Time.deltaTime, this way they won't be framerate dependend (as they currently are)
Fifth thing: I have a feeling you shouldn't be setting your target rotation based on normal at current position. The way you are doing it now your rotation lags behind - you should be raycasting from a position one step in the future from your current position, so you know what rotation to take so it is correct by the time you make that step. Currently you set target a future rotation to a rotation correct now, which will lag a frame. Alternatively you could just move the translate step to the top of the loop, the transform will update and the rest should flow as it does.
Finally, your image link doesn't work.
I hope that helps at all

Categories

Resources