Spawn an object and move it after some seconds along the cursor point in Unity 3D - unity3d

I am having a projectile object which I want to spawn and move it along the cursor direction after some seconds.
Basically if I don't wait after spawning the projectile, then the movement is working great. But since I am waiting for some seconds before I move the projectile, It actually travels backward and hitting the player.
here is the code.
void crossHairPosition(){
if (Physics.Raycast(ray, out hit, 100f, layer_Mask))
{
Debug.DrawLine(firePoint.position, hit.point, Color.green);
destination = hit.point;
crossHairPrefab.transform.position = ray.GetPoint(10);
crossHairPrefab.transform.LookAt(Camera.main.transform);
}
else {
destination = ray.GetPoint(1000);
}
}
void spawnProjectile(){
projectile=Instantiate(projectileObj,player.transform.position,Quaternion.identity);
StartCoroutine(waitforseconds(2));
}
IEnumerator waitforseconds(float time){
yield return new WaitForSeconds(time);
moveProjectile();
}
void moveProjectile(){
Vector3 difference=destination - projectile.transform.position;
float distance=difference.magnitude;
Vector3 direction=difference/distance;
direction.Normalize();
projectileObj.GetComponent<Rigidbody>().velocity = direction * 10f;
}

Just so the question has an answer, I'll write my comment out as a post. As there is an issue with delaying the direction calculation when firing the projectile, but no issue without a delay, cache the direction vector and pass it into the IEnumerator.
The code could look something like:
IEnumerator waitforseconds(float time)
{
// calculate the direction before the wait
Vector3 difference=destination - projectile.transform.position;
float distance=difference.magnitude;
Vector3 direction=difference/distance;
direction.Normalize();
yield return new WaitForSeconds(time);
// pass in our pre-calculated direction vector
moveProjectile(direction);
}
void moveProjectile(Vector3 direction)
{
projectileObj.GetComponent<Rigidbody>().velocity = direction * 10f;
}

Related

Rotating an object around the same pivot gives different results

Rotating an object around the same pivot gives different results
So I'm trying to make a door that opens when clicked on, and I have a door that works good as is, but for some reason when I try to make it work for another door using RotateAround, it moves along the Z axis when closing. The code is identical to the other door that works and works when opening this door, so I have no clue as to why it's having trouble closing for this one. The code I'm using to rotate them is as follows
IEnumerator CloseDoor()
{
float timer = 1f;
float speed = 30f;
Vector3 pivot = new Vector3(door.transform.position.x, door.transform.position.y,
door.transform.position.z + 1);
while (timer > 0)
{
door.transform.RotateAround(pivot, Vector3.down, speed * Time.deltaTime);
yield return new WaitForSeconds(0.001f);
timer -= Time.deltaTime;
}
}
IEnumerator OpenDoor()
{
float timer = 1f;
float speed = 30f;
Vector3 pivot = new Vector3(door.transform.position.x, door.transform.position.y,
door.transform.position.z + 1);
while (timer > 0)
{
door.transform.RotateAround(pivot, Vector3.up, speed * Time.deltaTime);
yield return new WaitForSeconds(0.001f);
timer -= Time.deltaTime;
}
}
I'll start with some overall advices, regarding your code.
Don't use Time.deltaTime with new WaitForSeconds(...). Time.deltaTime is an amout of time, that passed between Update() calls. But your logic is not inside Update(). You use own time inervals with new WaitForSecons(...), but if the specified amout of time is less then Time.deltaTime, then it will be executed every Time.deltaTime after all Update() calls. It works good for you only because your time interval 0.001f is low enough to be executed every Update(). When your argument in new WaitForSeconts(...) becomes more them Time.deltaTime, the rotation speed of the door becomes too low to door be opened completely. To made your code more clear and safe, return null with yield instruction. In this case, you can be shure, that coroutine will be executed every Update(). More info here.
Your code works with transform positioning, and probably you have physics in your game. All changes with physical object supposed to be done in the FixedUpdate(). In your case your cant return new WaitForFixedUpdate() in yield instruction and use Time.fixedDeltaTime with it.
So speaking about main question. In your code, you are doing pretty unclear and unsafe thing, like hardcoding pivot with global position offset here:
Vector3 pivot = new Vector3(door.transform.position.x, door.transform.position.y,
door.transform.position.z + 1);
Probably, not all doors will have same rotation, and for some of them offset, just with z coordinate will be wrong. Also it becomes wrong after door opening, because the position and rotation of the door changed when you rotating it around some point, that is not the center of the door. So you should base on local transform point, like this:
public class Door : MonoBehaviour
{
private bool _doorOpened = false;
private bool _doorOpening = false;
[SerializeField] // to see in the inspector.
private Vector3 _localDoorRotatePoint = new Vector3(0.5f, 0f, 0f);
// Update is called once per frame
void Update()
{
if (!_doorOpening && Input.GetKeyDown(KeyCode.E))
{
if (!_doorOpened)
{
StartCoroutine(OpenDoor());
}
else
{
StartCoroutine(CloseDoor());
}
}
}
IEnumerator CloseDoor()
{
_doorOpening = true;
var timer = 1f;
var speed = 30f;
// in my case _localDoorRotate point is (0.5f, 0f, 0). In your case it will be like (0, 0, 1f) or something like this.
// remember, that this point is in local transform coordinates, and in scales with this transform scale vector.
var pivot = transform.TransformPoint(_localDoorRotatePoint);
while (timer > 0)
{
transform.RotateAround(pivot, Vector3.down, speed * Time.fixedDeltaTime);
yield return new WaitForFixedUpdate();
timer -= Time.fixedDeltaTime;
}
_doorOpening = false;
_doorOpened = false;
}
IEnumerator OpenDoor()
{
_doorOpening = true;
var timer = 1f;
var speed = 30f;
// in my case _localDoorRotate point is (0.5f, 0f, 0f). In your case it will be like (0f, 0f, 1f) or something like this.
// remember, that this point is in local transform coordinates, and in scales with this transform scale vector.
var pivot = transform.TransformPoint(_localDoorRotatePoint);
while (timer > 0)
{
transform.RotateAround(pivot, Vector3.up, speed * Time.fixedDeltaTime);
yield return new WaitForFixedUpdate();
timer -= Time.fixedDeltaTime;
}
_doorOpening = false;
_doorOpened = true;
}
}
Helpfull links, that can help you with understanding this code:
Transform.TransformPoint
Transform.up

Unity2D - Rigidbody not stopping when it reaches a position

I have this code:
public class MoveCard : MonoBehaviour
{
public float speed = 1f;
public Rigidbody2D rb;
public Vector2 pos = new Vector2(6.8f,0);
public bool move = false;
void FixedUpdate(){
if (move){
//Stops Rigidbody
if (rb.position == pos){
move = false;
}
rb.transform.position += -rb.transform.right * speed * Time.fixedDeltaTime;
}
}
public void CardMovement(){
move = true;
}
}
I have it set as so when a button is pressed, CardMovement() initiates and in FixedUpdate I have a if statement that turns move off when the Rigidbody reaches a certain position. The rb moves but it doesn't stop when it reaches the Vector2. I am new to Unity so I don't know if this is the way to do it.
Well your Rb doesn't pass exactly for each point between the initial position and Vector2.
It's prtty unlikely to have rb.position == pos because one frame it will not enough, and the next one will be too much :)
Try with MoveTowards. Some like this:
rb.position = Vector3.MoveTowards(rb.position, pos, speed * Time.fixedDeltaTime);
You dont need a statement to stop it because it will do it when reaches pos.
PD: You can do this with transform instead of rigidbody if u are not going to use physics and you only want a movement.
Don't compare the 2 vector2D values like this:
if(rb.position == pos)
Rather compare the distance between them with a value that is very small, like this:
if(Vector2.Distance(rb.position,pos) <= 0.01)
Additionally, you can set the position like this rb.postion = pos; if it is close enough so that it snaps to the right location.

Unity 2D - Tap Image to accelerate car

I am making a Hill Climb Racing clone as a school project. Currently my car is accelerating with the A and D keys in my editor (movement = Input.GetAxis("Horizontal");), however the game has to be for Android, therefore like in the OG game, I added 2 sprites for brakes and pedals and added EventSystems to them eg.:
public void OnPointerDown(PointerEventData eventData)
{
gas.sprite = OnSprite_gas;
is_clicking = true;
}
and I dont know how to change acceleration to when the gas image is clicked and held down and how to brake (but not go backwards) when the brake is held down.
You seem to be on the right track.
In the car's Update() method, you will want to check if the brake or accelerator button is_clicking property is set, and handle the movement force.
This could look something like:
void Update()
{
if (accelerator.is_clicking)
{
movement = new Vector3(1f, 0f, 0f) * speed;
}
else if (brake.is_clicking)
{
movement = new Vector3(-1f, 0f, 0f) * speed;
}
else
{
movement = new Vector3(0f, 0f, 0f);
}
}
void FixedUpdate()
{
rb.AddForce(movement * Time.fixedDeltaTime);
}
You could then check if the velocity is close to 0 to stop applying the brake force.

Unity Mouse Input without Ray/Raycast and Colliders

First of all thank you for your time. I'm quite new in this so I'm struggling a bit. I'm trying to make a drag and release shooter which doesn't really depend on colliders or raycasts but solely depends on mouse delta and camera position. The way I'm trying to have it done is I'm mapping mouse position (x,y) to velocity direction such as new Vector3(mouseX, 0f, mouseY) then rotating it about Y axis to match the visual drag on the screen.
void OnMouseUp()
{
releasePos = Input.mousePosition;
Vector2 m_direction = releasePos - initialPos;
Vector3 direction = new Vector3(m_direction.x, 0, m_direction.y);
float userDrag = Mathf.Clamp(direction.magnitude, 0f, maxVelocity);
direction = Translation(direction);
direction = Vector3.Normalize(direction);
rigidbody.velocity = (direction * userDrag) * force;
}
private Vector3 Translation(Vector3 direction)
{
Vector3 camPos = Camera.main.transform.position;
camPos.y = 0;
float angle = Vector3.Angle(new Vector3(0f, 0f, 1f), camPos);
Quaternion translatedAngle = Quaternion.AngleAxis(angle, Vector3.up);
direction = translatedAngle * direction;
But as the angle changes it kinda fails to deliver what I'm asking for. Is there a way I can avoid bunch of if, else statements for the angle value or a shorter way of doing this?
Example
OnMouseUp is fundamentally a collider method.
Meaning, the mouse MUST be over the GameObject's collider for the GameObject this code is attached to. If you want to move away from using colliders, then you need to move away from this method.
The generic works-anywhere way of saying "is the mouse button up or down?" is the static methods available in the Input class:
Input.GetMouseButtonDown() True on the single frame after the mouse button is depressed
Input.GetMouseButtonUp() True on the single frame after the mouse button is released
Input.GetMouseButton() True any frame the mouse button is depressed (otherwise false)
So I think I have a solution finally and I thought I'd share it for people who might be interested.
The challenge was to rotate mouse delta's direction according to camera position so it gives a natural vibe to the player when dragging and releasing the ball for putting. I did some tests to see where my code had problems rotating delta direction properly(so it matches the screen) and realized when I'm "looking from" (0,0,1) for comparison while the camera's x position is positive it works fine. But when x is negative it doesn't because Vector3.Angle doesn't return a negative value as far as I'm concerned so I just multiplied the result with minus. Seems to work.
Here is the final code:
private void Start()
{
rigidbody = GetComponent<Rigidbody>();
}
void OnMouseDown()
{
ballPos = Input.mousePosition;
}
void OnMouseUp()
{
Vector2 m_direction = Input.mousePosition - ballPos;
Vector3 direction = new Vector3(m_direction.x, 0, m_direction.y);
float userDrag = Mathf.Clamp(direction.magnitude, 0f, maxVelocity);
direction = Translation(direction);
direction = Vector3.Normalize(direction);
rigidbody.velocity = (direction * userDrag) * force;
}
private Vector3 Translation(Vector3 direction)
{
float angle = GetAngle();
Quaternion translatedAngle = Quaternion.AngleAxis(angle, Vector3.up);
direction = translatedAngle * direction;
return direction;
}
private float GetAngle()
{
Vector3 camPos = Camera.main.transform.position;
camPos.y = 0;
if (camPos.x > 0)
{
angle = Vector3.Angle(Vector3.forward, camPos);
}
else if (camPos.x <0)
{ angle = -Vector3.Angle(Vector3.forward, camPos); }
return angle;
}
please do share if you have suggestions regarding the code.

Photon 2d game multiplayer lags

I have a 2D multiplayer game using Photon where all the movements are made by RigidBody 2d. When both players are connected I can see my opponent movements but they are not smooth. Last Update Time variable is set to 0.25.
using UnityEngine;
using System.Collections;
public class NetworkCharacter : Photon.MonoBehaviour {
Vector3 realPosition = Vector3.zero;
Quaternion realRotation = Quaternion.identity;
public float lastUpdateTime = 0.1f;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void FixedUpdate () {
if(photonView.isMine){
//Do nothing -- we are moving
}
else {
transform.position = Vector3.Lerp(transform.position, realPosition, lastUpdateTime);
transform.rotation = Quaternion.Lerp(transform.rotation, realRotation, lastUpdateTime);
}
}
public void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info){
if(stream.isWriting){
//This is OUR player.We need to send our actual position to the network
stream.SendNext(transform.position);
stream.SendNext(transform.rotation);
}else{
//This is someone else's player.We need to receive their positin (as of a few millisecond ago, and update our version of that player.
realPosition = (Vector3)stream.ReceiveNext();
realRotation = (Quaternion)stream.ReceiveNext();
}
}
}
From the unity script reference
[Lerp] Interpolates between from and to by the fraction t. This is most commonly used to find a point some fraction of the way along a line between two endpoints (eg, to move an object gradually between those points). This fraction is clamped to the range [0...1]. When t = 0 returns from. When t = 1 returns to. When t = 0.5 returns the point midway between from and to.
So basically Vector3.Lerp returns this:
from + ((to - from) * t)
As you can see, by calling Lerp with the same values, it's always returning the same value, what you should use instead are MoveTowards and RotateTowards.
transform.position = Vector3.MoveTowards(transform.position, realPosition, lastUpdateTime * Time.deltaTime);
transform.rotation = Quaternion.RotateTowards(transform.rotation, realRotation, lastUpdateTime * Time.deltaTime);