Collision detection script fails sometimes - c#

For the most part this script works, however every now and then an enemy will fail at pathfinding and go through a building or wall. Is there a way i can stop this?
using UnityEngine;
using System.Collections;
namespace Daniel {
public class EnemyAI : Living {
// Detection private int range = 10; private float speed = 10f; private bool isThereAnyThing = false;
// Waypoints/Targets
public GameObject[] targets;
private float rotationSpeed = 900f;
private RaycastHit hit;
GameObject target;
[SerializeField]
private int randomTarget = 0;
[SerializeField]
float timeToNextCheck = 3;
public float effectTimer = 2f;
public GameObject deathEffect;
public LayerMask detectThis;
void Start()
{
randomTarget = Random.Range(0, 8);
target = targets[randomTarget];
}
void FixedUpdate()
{
timeToNextCheck = timeToNextCheck - Time.deltaTime;
ScanForNewWaypoint();
LookAtTarget();
Move();
CheckForObsticales();
}
void LookAtTarget()
{
//Look At Somthly Towards the Target if there is nothing in front.
if (!isThereAnyThing)
{
Vector3 relativePos = target.transform.position - transform.position;
Quaternion rotation = Quaternion.LookRotation(relativePos);
transform.rotation = Quaternion.Slerp(transform.rotation, rotation, Time.deltaTime);
}
}
void Move()
{
// Enemy translate in forward direction.
transform.Translate(Vector3.forward * Time.deltaTime * speed);
}
public void CheckForObsticales()
{
//Checking for any Obstacle in front.
// Two rays left and right to the object to detect the obstacle.
Transform leftRay = transform;
Transform rightRay = transform;
//Use Phyics.RayCast to detect the obstacle
if (Physics.Raycast(leftRay.position + (transform.right * 7f), transform.forward, out hit, range, detectThis) || Physics.Raycast(rightRay.position - (transform.right * 7f), transform.forward, out hit, range))
{
if (hit.collider.gameObject.CompareTag("Obstacles"))
{
isThereAnyThing = true;
transform.Rotate(Vector3.up * Time.deltaTime * rotationSpeed);
}
}
// Now Two More RayCast At The End of Object to detect that object has already pass the obsatacle.
// Just making this boolean variable false it means there is nothing in front of object.
if (Physics.Raycast(transform.position - (transform.forward * 4), transform.right, out hit, 10, detectThis) ||
Physics.Raycast(transform.position - (transform.forward * 4), -transform.right, out hit, 10, detectThis))
{
if (hit.collider.gameObject.CompareTag("Obstacles"))
{
isThereAnyThing = false;
}
}
}
public void ScanForNewWaypoint()
{
CheckForObsticales();
if (timeToNextCheck <= 0)
{
timeToNextCheck = Random.Range(6, 3);
randomTarget = Random.Range(0, 8);
target = targets[randomTarget];
}
}
public override void TakeHit(float dmg, Vector3 hitPoint, Vector3 hitDirection)
{
if (dmg >= health)
{
Destroy(Instantiate(deathEffect, hitPoint, Quaternion.FromToRotation(Vector3.forward, hitDirection)) as GameObject, effectTimer);
Debug.Log("Exploded");
}
base.TakeHit(dmg, hitPoint, hitDirection);
}
}
}

Okay now this is one of the most common problems people face when playing with Physics Engine and believe me there are lot of factors that adds up to the result/consequences you are facing. Most common solutions devised by people is to use Update which is Executed Every Frame for Physics calculation which is Entirely Wrong. All the physics related calculation needs to be performed inside the Fixed Update otherwise it will be too expensive to execute physics based simulations in the Update which means every single frame. In your case where your Enemy is going through walls and Physics Collisions are missing the best solution you can take is to tweak some Physics Related Properties in Unity these are located in Edit>>>Project Settings>>>Time change the Time Step and Maximum Allowed Time Step in the inspector to set your collisions right. There are some other things to keep in mind too while dealing with physics like scale, mass and colliders etc. Here is a Link concerning the detailed mechanics of the physics settings in unity. Make sure you read and understand the contents of the link before working and tweaking physics settings.

Related

FPS Projectile firing from the wrong place

I'm trying to make a basic FPS game in Unity and I'm having an issue where the projectiles I shoot won't instantiate in the right place. From what I can tell, the projectile instantiates in roughly the same position relative to the player regardless of where I look (that position being a little to the left of the starting angle of the player).
Here's a 20 second video demonstration of what I'm talking about.
https://youtu.be/WLVuqUtMqd0
Even when I'm facing the exact opposite direction of where the projectile usually instantiates it still spawns in the same place, which means the projectile ends up spawning behind the player and hitting the player.
I tried using Debug.DrawRay() to see if maybe the firepoint itself is wrong (which would be shown by the ray starting somewhere other than the gun barrel). However it seems like the starting point of the ray is correct every time.
I'm not sure if this is related to the issue above, but I have noticed that the ray direction is wrong if I'm looking a little below the horizon. It seems like the projectile's direction is correct regardless though.
Ray directions when looking slightly above the horizon vs. lower
Here's my code for intantiating/shooting the projectile. I think the most relevant parts are probably shootProjectile() and instantiateProjectile(). This script is attached to the player character.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Shooting : MonoBehaviour
{
public Camera cam;
public GameObject projectile;
public Transform firePoint;//firePoint is set to the end of the barrel.
public float projectileSpeed = 40;
//private var ray;
private Vector3 destination;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
//var ray = cam.ViewportPointToRay(new Vector3(0.5f,0.5f,0));
//Debug.DrawRay(ray.origin, ray.direction);
if(Input.GetButtonDown("Fire1")) {
//player is shooting
ShootProjectile();
}
}
void ShootProjectile() {
Ray ray1 = cam.ScreenPointToRay(Input.mousePosition);
//Debug.Log(ray.direction);
RaycastHit hit;
if(Physics.Raycast(ray1, out hit))//checking whether the player is going to hit something
{
destination = hit.point;
}
else {
destination = ray1.GetPoint(1000);
}
Debug.DrawRay(firePoint.position, destination, Color.white, 10f);
InstantiateProjectile(firePoint);
}
void InstantiateProjectile(Transform firePoint) {
var projectileObj = Instantiate (projectile, firePoint.position, Quaternion.identity) as GameObject;//projectile is instantiated
projectileObj.GetComponent<Rigidbody>().velocity = (destination - firePoint.position).normalized * projectileSpeed;//projectile is set in motion
}
}
Here's the location of firePoint.
firePoint (i.e. where the projectile should instantiate)
I would appreciate any help on this, as I've been trying to fix it (on and off) for several days, and really have no idea what the problem is.
Edit: Here's my player movement script(s) as well. The first one is PlayerController.cs, and it converts the player's inputs into the appropriate movement vectors and camera rotation. It then calls methods from PlayerMotor.cs, which actually performs the movements.
PlayerController
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(PlayerMotor))]
public class PlayerController : MonoBehaviour
{
[SerializeField]
public float speed = 10f;
[SerializeField]
private float lookSens = 3f;
private PlayerMotor motor;
void Start() {
motor = GetComponent<PlayerMotor>();
//Debug.Log("PlayerControllerStart");
Cursor.lockState = CursorLockMode.Locked;
}
void Update() {
//Debug.Log("PlayerControllerUpdate");
//calculate movement velocity as 3D vector.
float xMov = Input.GetAxisRaw("Horizontal");
float zMov = Input.GetAxisRaw("Vertical");
Vector3 movHorizontal = transform.right * xMov;
Vector3 movVertical = transform.forward * zMov;
Vector3 velocity = (movHorizontal + movVertical).normalized * speed;
motor.move(velocity);
//speed*=(float)1.15;
//rotation
float yRot = Input.GetAxisRaw("Mouse X");
Vector3 rotation = new Vector3 (0f, yRot, 0f) * lookSens;
motor.rotate(rotation);
float xRot = Input.GetAxisRaw("Mouse Y");
Vector3 cameraRotation = new Vector3 (xRot, 0f, 0f) * lookSens;
motor.rotateCamera(cameraRotation);
if (Input.GetKeyDown(KeyCode.Space) == true && motor.isGrounded()) {
motor.canJump=true;
}
if (Input.GetKey(KeyCode.W) == true) {
motor.accel=true;
}
else{
motor.accel=false;
}
}
}
PlayerMotor:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(Rigidbody))]
public class PlayerMotor : MonoBehaviour
{
[SerializeField]
private Camera cam;
private Vector3 velocity = Vector3.zero;
private Vector3 rotation = Vector3.zero;
private Vector3 cameraRotation = Vector3.zero;
private Vector3 jumpVector = new Vector3 (0f, 5f, 0f);
private PlayerController pc;
private Rigidbody rb;
public bool canJump;
public bool accel;
public float acceleration;
int jumpCount;
void Start() {
rb = GetComponent<Rigidbody>();
pc = GetComponent<PlayerController>();
canJump=false;
jumpCount=0;
accel=false;
//acceleration = 1.0;
//distToGround = collider.bounds.extents.y;
//Debug.Log("PlayerMotorStart");
}
//sets velocity to a given movement vector.
public void move(Vector3 _velocity) {
velocity = _velocity;
}
public void rotate(Vector3 _rotation) {
rotation = _rotation;
}
public void rotateCamera(Vector3 _cameraRotation) {
cameraRotation = _cameraRotation;
}
public bool isGrounded() {
return Physics.Raycast(transform.position, -Vector3.up, (float)2.5);
}
public void jump() {
rb.AddForce(transform.up * 250f);
//Debug.Log("Jump"+jumpCount);
jumpCount++;
canJump=false;
}
void FixedUpdate() {
performMovement();
performRotation();
if (canJump) {
jump();
}
//Debug.Log("PlayerMotorUpdate");
if(accel&&pc.speed<20f&&isGrounded())
{
//Debug.Log("W Pressed");
pc.speed*=(float)1.005;
}
else if(!accel) {
pc.speed=7f;
}
}
void performMovement() {
if(velocity != Vector3.zero) {
rb.MovePosition(rb.position + velocity * Time.fixedDeltaTime);
//Debug.Log("Movement");
}
}
void performRotation() {
rb.MoveRotation(rb.rotation * Quaternion.Euler(rotation));
if(cam != null) {
cam.transform.Rotate(-cameraRotation);
}
}
}
Here are some pictures of my projectile prefab as well.
To solve one first confusion: The method Debug.DrawRay takes as paramters
a start position
a ray direction(!)
You are passing in another position which is not what you want.
This might only work "accidentally" as long as you stand on 0,0,0 so maybe it wasn't that notable but the debt ray definitely points into a wrong direction.
Rather do e.g.
Debug.DrawRay(firePoint.position, destination - firePoint.position, Color.white, 10f);
Or instead rather use Debug.DrawLine which rather actually takes
start position
end position
Then you don't need to calculate the direction first
Debug.DrawLine(firePoint.position, destination, Color.white, 10f);
Then I suspect the following is happening: The spawned projectileObj actually is placed correctly but it is the visuals inside that projectile prefab that have a local offset against the parent prefab root.
Since you always spawn the projectile in world rotation Quaternion.identity that offset stays the same no matter where you look.
I guess a simple fix would already be to also apply the same rotation like
var projectileObj = Instantiate (projectile, firePoint.position, firePoint.rotation);

How can I add velocity to a player in Unity?

I'm currently making a 2D game and I want to add a bounce pad. As a reference, I used the platformer microgame asset from the asset store. My problem is, that my player doesn't have velocity. Can someone explain me pls how I can do this? Or could you add the code to my current script because I'm not very keen on coding.
Thanks!
Current script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour {
public CharacterController2D controller;
public Animator animator;
public float runSpeed = 40f;
float horizontalMove = 0f;
bool jump = false;
bool crouch = false;
// Update is called once per frame
void Update () {
horizontalMove = Input.GetAxisRaw("Horizontal") * runSpeed;
animator.SetFloat("Speed", Mathf.Abs(horizontalMove));
if (Input.GetButtonDown("Jump"))
{
jump = true;
animator.SetBool("IsJumping", true);
}
if (Input.GetButtonDown("Crouch"))
{
crouch = true;
} else if (Input.GetButtonUp("Crouch"))
{
crouch = false;
}
}
public void OnLanding ()
{
animator.SetBool("IsJumping", false);
}
public void OnCrouching (bool isCrouching)
{
animator.SetBool("IsCrouching", isCrouching);
}
void FixedUpdate ()
{
// Move our character
controller.Move(horizontalMove * Time.fixedDeltaTime, crouch, jump);
jump = false;
}
}
You should use Raycast to go directly down and see if the player is above a “bounce pad”. Then calculate the distance, normal, and force in the code below:
float bounceDist = 0.2f; //max distance to hit the bounce pad.
LayerMask bounceMask; //assign mask to bounce pads.
float force = 100f; //how much force to apply when hit a bounce pad.
void Update()
{
RaycastHit2D hit; //declaring a variable to store Raycast information.
if (Physics2D.Raycast(transform.position, -Vector2.up, out hit, bounceDist, bounceMask))
{
float dist = Vector2.Distance(transform.position, hit.collider.gameObject.transform.position);
dist = -dist + (bounceDist + bounceDist - 0.15f);
controller.Move(hit.normal * Time.deltaTime * dist * force);
}
}
This makes an if statement that runs if the player hit something that is directly below it with a certain distance (bounceDist) and another certain mask (bounceMask). Then it moves the controller by whatever direction is up on bounce pad (normal). (For example: if it was rotated 45°, then the player would be launched the same up distance as sideways.) Then it gets multiplied by Time.deltaTime, which makes it so the force is applied the same amount even with different frame rates. Then the force is multiplied by the value of force, just to add force. Then it is multiplied by the distance, which I made greater when closer. This can be removed, but might be useful. If you get an error with the if statement, you should change the code to use the if statement to check if the tag on the hit object was a certain tag:
You should use Raycast to go directly down and see if the player is above a “bounce pad”. Then calculate the distance, normal, and force in the code below:
float bounceDist = 0.2f; //max distance to hit the bounce pad.
float force = 100f; //how much force to apply when hit a bounce pad.
void Update()
{
RaycastHit2D hit; //declaring a variable to store Raycast information.
Physics2D.Raycast(transform.position, -Vector2.up, out hit, bounceDist, bounceMask);
if (hit.collider.gameObject.tag == “bouncePad”)
{
float dist = Vector2.Distance(transform.position, hit.collider.gameObject.transform.position);
dist = -dist + (bounceDist + bounceDist - 0.15f);
controller.Move(hit.normal * Time.deltaTime * dist * force);
}
}

Teleport while Controlling GameObject in Unity3D

I am a freshman design student and they've asked us to create a game on unity3D without much training on it so needless to say I don't know much except for the super basic stuff. I don't know anything about c# and I've been having an issue making a gameobject teleport. I've spent 6 hours searching for a solution online and the only conclusion I got to was that my object is probably having issues teleporting because of the way I am controlling it - something to do with the controller remembering the last position before the teleport and returning to it. I have no idea how to fix it though.
So this is what my scene looks like: I have a sphere as my character, I move it to this other object that has a collider as trigger which then teleports my sphere to a different point (black object) on the terrain. As soon as my object reaches there, it starts sliding back to the point where the teleport happened. I even tried edit > project settings > physics > auto sync transforms as many suggested that and it worked for them.
This is the code by which I control my player:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MyPlayer : MonoBehaviour
{
public float speed = 1;
public float spacing = 1;
private Vector3 pos;
// Use this for initialization
void Awake()
{
pos = transform.position;
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.W))
pos.x += spacing;
if (Input.GetKeyDown(KeyCode.S))
pos.x -= spacing;
if (Input.GetKeyDown(KeyCode.D))
pos.z -= spacing;
if (Input.GetKeyDown(KeyCode.A))
pos.z += spacing;
transform.position = Vector3.MoveTowards(transform.position, pos, speed * Time.deltaTime);
}
}
and I also have a camera that follows the sphere using this code
using UnityEngine;
using System.Collections;
public class CompleteCameraController : MonoBehaviour {
public GameObject player; //Public variable to store a reference to the player game object
private Vector3 offset; //Private variable to store the offset distance between the player and camera
// Use this for initialization
void Start ()
{
//Calculate and store the offset value by getting the distance between the player's position and camera's position.
offset = transform.position - player.transform.position;
}
// LateUpdate is called after Update each frame
void LateUpdate ()
{
// Set the position of the camera's transform to be the same as the player's, but offset by the calculated offset distance.
transform.position = player.transform.position + offset;
}
}
and I have another code on the camera that makes me be able to look around using my mouse
using UnityEngine;
using System.Collections;
public class FlyCamera : MonoBehaviour
{
/*
Writen by Windexglow 11-13-10. Use it, edit it, steal it I don't care.
Converted to C# 27-02-13 - no credit wanted.
Simple flycam I made, since I couldn't find any others made public.
Made simple to use (drag and drop, done) for regular keyboard layout
wasd : basic movement
shift : Makes camera accelerate
space : Moves camera on X and Z axis only. So camera doesn't gain any height*/
float mainSpeed = 700.0f; //regular speed
float shiftAdd = 950.0f; //multiplied by how long shift is held. Basically running
float maxShift = 2000.0f; //Maximum speed when holdin gshift
float camSens = 0.25f; //How sensitive it with mouse
private Vector3 lastMouse = new Vector3(255, 255, 255); //kind of in the middle of the screen, rather than at the top (play)
private float totalRun = 1.0f;
void Update()
{
lastMouse = Input.mousePosition - lastMouse;
lastMouse = new Vector3(-lastMouse.y * camSens, lastMouse.x * camSens, 0);
lastMouse = new Vector3(transform.eulerAngles.x + lastMouse.x, transform.eulerAngles.y + lastMouse.y, 0);
transform.eulerAngles = lastMouse;
lastMouse = Input.mousePosition;
//Mouse camera angle done.
//Keyboard commands
float f = 0.0f;
Vector3 p = GetBaseInput();
if (Input.GetKey(KeyCode.LeftShift))
{
totalRun += Time.deltaTime;
p = p * totalRun * shiftAdd;
p.x = Mathf.Clamp(p.x, -maxShift, maxShift);
p.y = Mathf.Clamp(p.y, -maxShift, maxShift);
p.z = Mathf.Clamp(p.z, -maxShift, maxShift);
}
else
{
totalRun = Mathf.Clamp(totalRun * 0.5f, 1f, 1000f);
p = p * mainSpeed;
}
p = p * Time.deltaTime;
Vector3 newPosition = transform.position;
if (Input.GetKey(KeyCode.Space))
{ //If player wants to move on X and Z axis only
transform.Translate(p);
newPosition.x = transform.position.x;
newPosition.z = transform.position.z;
transform.position = newPosition;
}
else
{
transform.Translate(p);
}
}
private Vector3 GetBaseInput()
{ //returns the basic values, if it's 0 than it's not active.
Vector3 p_Velocity = new Vector3();
if (Input.GetKey(KeyCode.W))
{
p_Velocity += new Vector3(0, 0, 1);
}
if (Input.GetKey(KeyCode.S))
{
p_Velocity += new Vector3(0, 0, -1);
}
if (Input.GetKey(KeyCode.A))
{
p_Velocity += new Vector3(-1, 0, 0);
}
if (Input.GetKey(KeyCode.D))
{
p_Velocity += new Vector3(1, 0, 0);
}
return p_Velocity;
}
}
Please let me know if there's a specific part of my code that I need to edit to resolve this or alternatively if you have a different code that won't give me this issue, that would make my life so much easier. If I need to edit something or you're sharing a code, please respond with the complete (corrected) code because otherwise I will just be even more confused.
I know this is a super long post and I am sorry but I am really desperate. It's been really hard studying online and basically having to teach myself all of this. This is for a final project so I will really appreciate any help you can throw my way. Thank you for reading and thanks for any help in advance.
EDIT: The teleport code is executing fine because I do teleport to the chosen location, I just end up sliding back to the point which I teleported from.
This is the teleporting code I am using.
using UnityEngine;
using System.Collections;
public class Teleport : MonoBehaviour
{
public GameObject ui;
public GameObject objToTP;
public Transform tpLoc;
void Start()
{
ui.SetActive(false);
}
void OnTriggerStay(Collider other)
{
ui.SetActive(true);
if ((other.gameObject.tag == "Player") && Input.GetKeyDown(KeyCode.E))
{
objToTP.transform.position = tpLoc.transform.position;
}
}
void OnTriggerExit()
{
ui.SetActive(false);
}
}
Ok, so the main reason why your character is drifting back to original position is that the pos variable in the MyPlayer script stays the same after teleporting.
Remedy for that will be changing pos variable from Teleport script after objToTP.transform.position = tpLoc.transform.position;. Something like objToTP.gameobject.GetComponent<MyPlayer>().pos = tpLoc.transform.position;
But make sure that objToTP has component MyPlayer and pos in MyPlayer is public.
Once again: it's a simple way to resolve your problem. In a real project you should create more flexible architecture, but that's a different story.
I believe that you sliding back because you moving player with transform.position = Vector3.MoveTowards in Update().
And you moving it to coordinates that was got from your input

How to make a thrown object rotate automatically based on the direction its thrown in?

so what I am trying to do is pull a spear back(like a slingshot) and send it flying, all the while making it rotate while it's flying in the air to mimic a real thrown spear. So far, I have only been able to make it rotate after a few seconds to a certain position but I can already tell that this isn't really a permanent fix and will definitely lead to problems down the line. I have tried looking for better ways of rotating the object but I can't really find anybody else who had a problem like this and it's hard to understand the unity documentation regarding rotation as it's for 3D objects.
Here is a gif showing how it looks like:
https://gfycat.com/chiefglasshorsemouse
This is the code that I have attached to my spear object that's responsible for letting me pull it back and launch it with the mouse:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.Scripting.APIUpdating;
public class spear : MonoBehaviour
{
Rigidbody2D rb;
Vector3 velocity;
private Vector3 _initialPosition;
[SerializeField] private float _launchPower = 500;
bool rotating = false;
public GameObject objectToRotate;
private float _timeSittingAround;
private bool _spearWasLaunched;
[SerializeField] private float _spearRotation = 360;
private void Awake()
{
_initialPosition = transform.position;
rb = GetComponent<Rigidbody2D>();
}
void Start()
{
rb = GetComponent<Rigidbody2D>();
}
//rotates the spear
IEnumerator rotateObject(GameObject gameObjectToMove, Quaternion newRot, float duration)
{
if(rotating)
{
yield break;
}
rotating = true;
Quaternion currentRot = gameObjectToMove.transform.rotation;
float counter = 0;
while(counter < duration)
{
counter += Time.deltaTime;
gameObjectToMove.transform.rotation = Quaternion.Lerp(currentRot, newRot, counter / duration);
yield return null;
}
rotating = false;
}
//reloads the scene if spear goes out of bounds or lays dormant for 2 seconds
void Update()
{
GetComponent<LineRenderer>().SetPosition(0, transform.position);
GetComponent<LineRenderer>().SetPosition(1, _initialPosition);
if (_spearWasLaunched &&
GetComponent<Rigidbody2D>().velocity.magnitude <= 0.1)
{
_timeSittingAround += Time.deltaTime;
}
if(transform.position.y > 30 ||
transform.position.y < -12.5 ||
transform.position.x > 40 ||
transform.position.x < -20 ||
_timeSittingAround > 2)
{
string currentSceneName = SceneManager.GetActiveScene().name;
SceneManager.LoadScene(currentSceneName);
}
}
//slingshot mechanic
private void OnMouseDrag()
{
Vector3 newPosition = Camera.main.ScreenToWorldPoint(Input.mousePosition);
transform.position = new Vector3(newPosition.x, newPosition.y);
}
private void OnMouseDown()
{
GetComponent<SpriteRenderer>().color = Color.red;
GetComponent<LineRenderer>().enabled = true;
}
//launches the spear when mouse is released as well as begins the rotating mechanic
private void OnMouseUp()
{
Vector2 directionToInitialPosition = _initialPosition - transform.position;
GetComponent<Rigidbody2D>().AddForce(directionToInitialPosition * _launchPower);
GetComponent<Rigidbody2D>().gravityScale = 1;
_spearWasLaunched = true;
GetComponent<LineRenderer>().enabled = false;
Quaternion rotation2 = Quaternion.Euler(new Vector3(0, 0, _spearRotation));
StartCoroutine(rotateObject(objectToRotate, rotation2, 3f));
}
}
There are a few ways to do it. You could try setting up rigidbodies and maybe joints and weighting different parts so that the tip of the spear naturally rotates the spear. More info on that here: https://answers.unity.com/questions/14899/realistic-rotation-of-flying-arrow.html
There's also another solution in that link for doing it with one line of code, making it rotate in the direction of movement.
In the top of your class, add this:
public Rigidbody2D mySpear;
Then in the inspector, drag the spear object into that slot to assign it. Then in your code, remove all your rotation code. In Update() add this:
mySpear.transform.right =
Vector2.Slerp(mySpear.transform.right, mySpear.rigidbody.velocity.normalized, Time.deltaTime)
A third way to do it is...
Figure out the rotation (on the z axis) of the object when it's pointing directly down. Put that down as a constant called downRotation or something.
Set a minimum velocity and maximum velocity that will be used by the rotation.
I recommend also adding an AnimationCurve variable.
Instead of having a special function for rotation, just put it in FixedUpdate. If the spear's velocity.x is not 0, then rotate.
The speed of the rotation should be based on the speed of the object. If the object's x velocity is very slow then it would probably fall (rotate) more quickly. If it's going very fast, the rotation would not change much.
Don't use this exact code, but it'd look something like this:
// This part figures out how fast rotation should be based on how fast the object is moving
var curve = speedCurve.Evaluate(1 - ((rigidBody.velocity.x - minVelocity) / (maxVelocity - minVelocity)));
// Rotates the spear
var rotate = rigidBody.rotation.eulerAngles;
var speed = curve * rotateSpeed * Time.fixedDeltaTime;
rotate.z = Mathf.MoveTowards(rotate.z, downRotation, speed);
transform.rotation = Quaternion.Euler(rotate);
Side note: You should use rigidbody rotation like I am here, and you should be assigning components to variables in the inspector instead of calling GetComponent every frame.

My player prefab moves forward fine until it hits an object, what is going on?

I'm making a unity game based off of TierZoo, and while testing a movement script, i crashed into one of the rigidbody trees for fun... and that made the player prefab stop moving forward, and it started moving in odd directions Here is my code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class NewPlayerController : MonoBehaviour
{
private Rigidbody rb;
public GameObject player;
public float thrust = 1.0f;
// Start is called before the first frame update
void Start()
{
rb = GetComponent<Rigidbody>();
}
// Update is called once per frame
void Update()
{
if (Input.GetKey(KeyCode.W))
{
rb.AddRelativeForce(player.transform.right * thrust * 2.5f);
}
if (Input.GetKey(KeyCode.A)) { player.transform.Rotate(0, -1, 0); }
if (Input.GetKey(KeyCode.D)) { player.transform.Rotate(0, 1, 0); }
if (Input.GetKey(KeyCode.S))
{
rb.AddRelativeForce(player.transform.right * thrust * -2.5f);
}
}
}
If you can find a way to fix this, let me know.
You could fix the rotation also in Y direction by keeping track of it and setting it hard in FixedUpdate.
In general as said in the comments:
Whenever a Rigidbody is involved you should not
do movements through the transform component
do movements in Update
both breaks the physics engine and you might get strange and unexpected movements or simply no collisions working etc.
Rather do
- go through the Rigidbody component e.g. using MovePosition and MoveRotation
- do these in FixedUpdate
these keeps the physics intact and moves your Rigidbody only e.g. until it collides meanwhile.
Then for the rotation you have framerate-dependent values Rotate(0, -1, 0)! You rather want to use Time.deltaTime in order to get smooth framerate-independent rotation speed - not in 1 degree per frame but rather e.g. 45 degrees per second.
You script could look like
public class NewPlayerController : MonoBehaviour
{
// already reference this via the Inspector
[SerializeField] private Rigidbody rb;
public GameObject player;
public float thrust = 1.0f;
private void Awake()
{
// as fallback get it on runtime
if(!rb) rb = GetComponent<Rigidbody>();
}
bool moveLeft;
bool moveRight;
bool rotateLeft;
bool rotateRight;
private float angle;
void Update()
{
// Get User input here
// (in your case it would be also fine to do it in FixedUpdate.
// What doesn't work in FixedUpdate are mainly the one-time events like GetKeyDown)
moveRight = Input.GetKey(KeyCode.W);
rotateLeft = Input.GetKey(KeyCode.A);
rotateRight = Input.GetKey(KeyCode.D);
moveLeft = Input.GetKey(KeyCode.S);
}
private void FixedUpdate()
{
if(moveRight) rb.AddRelativeForce(rb.rotation * Vector3.right * thrust * 2.5f);
if(moveLeft) rb.AddRelativeForce(rb.rotation * Vector3.right * thrust * -2.5f);
// Now for the rotations keep track of what you rotated already
// and overwrite the rotation with the fix value:
if(rotateLeft)
{
// e.g. for rotating 45°/second
angle -= -45 * Time.deltaTime;
rb.MoveRotation(Quaternion.Euler(0, angle, 0));
}
if(rotateRight)
{
angle += 45 * Time.deltaTime;
rb.MoveRotation(Quaternion.Euler(0, angle, 0));
}
}
}

Categories

Resources