Problem
I have created a navigation system using Rigidbody and NavMesh. There is an agent that tries to follow me around. It has a capsule collider, a script (see below), a Rigidbody, and a NavMeshAgent component. It works by running the NavMesh calculations, getting the coordinates for the next place to move, disabling the NavMesh, and moving there using MovePosition(). This system works pretty well usually, but lately I have been experiencing problems. When the Agent moves around an obstacle, it pauses on the corners. I believe the reason for this is my code, especially my MovePosition() function, but I don't know how to fix it.
https://vimeo.com/731216013
Code
using UnityEngine;
using UnityEngine.AI;
public class PlayerController : MonoBehaviour
{
public Rigidbody rb;
public float m_Speed = 3f;
public Transform goal;
public NavMeshAgent navMesh;
public float maxSpeed = 5f;
void FixedUpdate()
{
MoveToNextPosition();
}
void MoveToNextPosition()
{
navMesh.enabled = true;
if (navMesh.isOnNavMesh)
{
NavMeshPath path = new NavMeshPath();
if(navMesh.CalculatePath(goal.position, path))
{
navMesh.path = path;
}
}
Vector3 pos = navMesh.steeringTarget;
navMesh.enabled = false;
//Store user input as a movement vector
Vector3 distance = pos - transform.position;
Vector3 direction = distance.normalized;
float speed = rb.velocity.magnitude;
Debug.Log(direction);
rb.MovePosition(transform.position + direction * Time.deltaTime * m_Speed);
}
}
I have a big list of what the issues with the navmesh can be, hence I am posting it as an answer:
Open your navigation panel, if you can't find one or accidentally closed the tab, go to Window > AI > Navigation. The Navigation tab looks like this:
Open the Bake tab and set your agent's radius and height according to its dimensions. At the bottom, click to expand Advanced options. In that, there's a field Min Region Area, increasing the value increases the area around the static obstacles where the navmesh agent cannot move.
Related
I am developing a VR game.
Is a fighting game, the player will be able the punch the enemies (on his fist will be a collider and a disabled object with the damage script).
I need a script that will activate another object (the one with damage script) - (placed on his fist) but just on certain speed or force (you know, like in real life- if the enemy will be touched with the hand he should not be damaged, just on high force or speed)
What is the best solution?
thank you!
Since your player's fist is not being controlled by the physics system, it will not be possible to read the velocity of the player's hand like you would a normal Rigidbody. That being said, you can still calculate the speed and handle all of what you wish to do in one script.
Here's an example:
[RequireComponent(typeof(DamageScript))]
public class HandSpeedMonitor : Monobehaviour
{
public float threshold;
DamageScript damageScript;
Vector3 lastPos;
public void Awake()
{
damageScript = this.GetComponent<DamageScript>();
}
public void Start()
{
lastPos = this.transform.position;
}
public void Update()
{
float velocity = (lastPos - this.transform.position).magnitude / Time.deltaTime;
if(!damageScript.enabled && velocity > threshold)
damageScript.enabled = true;
else if(damageScript.enabled)
damageScript.enabled = false;
}
}
However, since .magnitude is an expensive call, you may want to consider storing your "threshold" as a squared speed "sqrThreshold" and use .sqrMagnitude, since it removes the square root component of vector math (saving on processing).
I am not new to programming, but I am new to C#. I am experienced in Python, Java, and HTML. My game is 2D I have a game where my character currently has to touch the enemy to kill it. Now I added code for shooting a bullet to kill the enemy. I also want the bullet to be shot if the spacebar is pressed. The character is supposed to a shoot bullet in either direction. I have taken my code from an example my professor gave me which was originally Javascript and I converted it to C#. Unity no longer supports Javascript. The example code he gave me was basically a rocket shooting as many bullets as I clicked (clicking the mouse shoots bullets) to eliminate an enemy, however the rocket in that example does not move. In my game, the character moves, so the bullet has to get the position of the character. What is the correct code for getting the character position and shooting a bullet correctly?
I tested my game with my current code. The bullet is being spit out of nowhere (from the bottom of my background wallpaper [smack in the middle of the bottom] to a little below the wallpaper). Not even from the character...
Also, I added the Hit class script to my Bullet category in Unity.
Complete Camera Controller (no issues here at all)
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;
}
}
Complete Player Control Class (If you read the comments in the code that say the "BULLET CODE", that's new code I have placed in the game for the bullets.
using UnityEngine;
using System.Collections;
//Adding this allows us to access members of the UI namespace including Text.
using UnityEngine.UI;
public class CompletePlayerController : MonoBehaviour
{
public float speed; //Floating point variable to store the player's movement speed.
public Text countText; //Store a reference to the UI Text component which will display the number of pickups collected.
public Text winText; //Store a reference to the UI Text component which will display the 'You win' message.
private Rigidbody2D rb2d; //Store a reference to the Rigidbody2D component required to use 2D Physics.
private int count; //Integer to store the number of pickups collected so far.
Rigidbody2D bullet;
float speed2 = 30f; //BULLET CODE
// Use this for initialization
void Start()
{
//Get and store a reference to the Rigidbody2D component so that we can access it.
rb2d = GetComponent<Rigidbody2D> ();
//Initialize count to zero.
count = 0;
//Initialze winText to a blank string since we haven't won yet at beginning.
winText.text = "";
//Call our SetCountText function which will update the text with the current value for count.
SetCountText ();
}
//FixedUpdate is called at a fixed interval and is independent of frame rate. Put physics code here.
void FixedUpdate()
{
//Store the current horizontal input in the float moveHorizontal.
float moveHorizontal = Input.GetAxis ("Horizontal");
//Store the current vertical input in the float moveVertical.
float moveVertical = Input.GetAxis ("Vertical");
Rigidbody2D bulletInstance; //BULLET CODE
//Use the two store floats to create a new Vector2 variable movement.
Vector2 movement = new Vector2 (moveHorizontal, moveVertical);
//Call the AddForce function of our Rigidbody2D rb2d supplying movement multiplied by speed to move our player.
rb2d.AddForce (movement * speed);
if(Input.GetKeyDown(KeyCode.Space)&& Hit.hit == false) //BULLET CODE IN HERE
{
// ... instantiate the bullet facing right and set it's velocity to the right.
bulletInstance = Instantiate(bullet, transform.position, Quaternion.Euler(new Vector3(0,0,0)));
bulletInstance.velocity = new Vector2(speed2, 0);
bulletInstance.name = "Bullet";
}
}
//OnTriggerEnter2D is called whenever this object overlaps with a trigger collider.
void OnTriggerEnter2D(Collider2D other)
{
//Check the provided Collider2D parameter other to see if it is tagged "PickUp", if it is...
if (other.gameObject.CompareTag ("PickUp"))
{
//... then set the other object we just collided with to inactive.
other.gameObject.SetActive(false);
transform.localScale += new Vector3(0.1f, 0.1f, 0);
//Add one to the current value of our count variable.
count = count + 1;
//Update the currently displayed count by calling the SetCountText function.
SetCountText ();
}
}
//This function updates the text displaying the number of objects we've collected and displays our victory message if we've collected all of them.
void SetCountText()
{
//Set the text property of our our countText object to "Count: " followed by the number stored in our count variable.
countText.text = "Count: " + count.ToString ();
//Check if we've collected all 12 pickups. If we have...
if (count >= 12)
//... then set the text property of our winText object to "You win!"
winText.text = "You win!";
}
}
Hit code. All code in this class is made for the bullet.
using UnityEngine;
using System.Collections;
public class Hit : MonoBehaviour
{
GameObject[] gameObjects;
public static bool hit = false;
void Removal ()
{
gameObjects = GameObject.FindGameObjectsWithTag("Bullet");
for(var i= 0 ; i < gameObjects.Length ; i ++)
Destroy(gameObjects[i]);
}
void OnCollisionEnter2D ( Collision2D other )
{
if(other.gameObject.name=="Bullet")
{
Removal();
Destroy(gameObject);
hit = true;
}
}
}
I see several issues with the code you wrote. as #Kyle Delaney suggested, I'd also highly recommend you check out the Unity Learn website, and go through several tutorials before moving through. I've highlighted a few issues below that may help solve your problem, but you could really benefit from changing your approach in the first place to avoid many of these issues. See below.
In your camera controller class, why not make the offset public so you can set it yourself in the inspector>
In Player controller class:
changed:
if (Input.GetKeyDown(KeyCode.Space)) //Shoot bullet
{
// ... instantiate the bullet facing right and set it's velocity to the right.
Rigidbody2D bulletRB = Instantiate(bullet, transform.position, transform.rotation);
bulletRB.AddForce(new Vector2(speed2,0), ForceMode2D.Impulse);
}
On a click, this instantiates the bullet prefab and sets its transform to copy the position and rotation of the player. It then adds a force to it to the right. You should avoid setting the velocity manually with rigidbodies, this can cause unwanted behaviour. Use the Addforce/addTorque methods instead. The ForceMode says to ignore its mass and set its velocity.
Then delete your Hit class, and replace with this Bullet class, which you drag onto the bullet prefab. Your player shouldn't be in charge of checking for bullet hits. that's the bullet's job. The player just launches the bullets and then the bullets do what bullets do. this just checks to see if the bullet hit anything, if so it destroys it. You can make this more complicated if you want. I would recommend using layers to determine which layers the bullet checks collisions with. You probably don't want bullets destroying your terrain. and you definitely don't want bullets destroying the player itself!
public class Bullet : MonoBehaviour
{
private void OnCollisionEnter2D(Collision2D collision)
{
Destroy(collision.gameObject);
Destroy(this.gameObject);
}
}
Also you shouldn't need to set the name of the bullet, since your prefab bullet should already be named "bullet".
I hope this gets you started in the right direction. But based on your code, I HIGHLY recommend you work through the tutorials before continuing with any projects. The unity staff that make them are super helpful and the tutorials start off really simple but get really complicated fast, so you actually come away learning a ton!
Perhaps the problem has to do with the player's parent transform. You could try something like this to make sure the bullet has the right parent:
bulletInstance = Instantiate(bullet);
bulletInstance.transform.parent = transform.parent;
bulletInstance.transform.position = transform.position;
bulletInstance.velocity = new Vector2(speed2, 0);
bulletInstance.name = "Bullet";
If that doesn't work it may be worthwhile to check the player's Rect Transform to see where the anchors and pivot are. Perhaps the player's actual "position" coordinates aren't where you think they are.
Have you done the Space Shooter tutorial? It has a segment about shooting bullets.
I got a "cube" in Unity. This cube has a Trigger, when moving into it, the object gets grabbed into the air. This is working as a lift, you can see an example what I mean here. It is a small scene taken from Spyro 2.
https://youtu.be/f8wWMa4N5mE?t=643
The code I use is really small for now
private float liftSpeed = 10; // the speed, the object is flying up
private void OnTriggerStay(Collider col)
{
Rigidbody objectRigid = col.gameObject.GetComponent<Rigidbody>(); // get the rigidbody from the object in the trigger
if (objectRigid != null) // does it have a rigidbody?
objectRigid.velocity = new Vector3(objectRigid.velocity.x, liftSpeed, objectRigid.velocity.z); // make it fly in the air
}
So I have a lift, that totally works fine. But I when I rotate my lift I want it to work aswell.
Some examples (my gamne is 3D)
So how can I make my lift work for all "rotations"?
You can use transform.up to get the up direction of your lift, and then multiply by the lift speed.
objectRigid.velocity = transform.up * liftSpeed;
transform.up changes depending on how the object is rotated, so if your lift is rotated to the left, then the lift will carry objects to the left.
You can use the transforms RotateAround Method for a rotation like in the video clip.
Transform t = col.gameObject.GetComponent<Transform>();
transform.RotateAround(Vector3.zero, Vector3.up, 20 * Time.deltaTime);
The given snipet lets the object rotate around it's axis, which is pointing up.
Make the player child of the lift OnTriggerEnter.
Move the lift as you do
You can rotate the lift using RotateAround function. The player should also rotate along with the lift, as it's a child of the lift now.
make player's parent to null when it's out of lift trigger using OnTriggerExit
This is my solution to the problem.
The lift and the EndPoint will be Empty GameObjects. If you want the lift has a platform or a base you can add a GameObject as child of the lift with the shape you prefer.
You need to attach a Collider to the lift and set it as trigger.
Then you add the following script to the lift Empty GameObject
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MoveLift : MonoBehaviour {
public Transform end;
float speed = 4f;
bool liftActivated = false;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
if(liftActivated)
{
float step = speed * Time.deltaTime;
//To move up the lift
transform.position = Vector3.MoveTowards(transform.position, end.position, step);
//To spin the lift
transform.RotateAround(Vector3.up, 2 * Time.deltaTime);
//To stop spining the lift when it reaches the end of the path
if(transform.position.Equals(end.position))
liftActivated = false;
}
}
void OnTriggerEnter(Collider other){
liftActivated = true;
other.gameObject.transform.parent = this.transform;
other.gameObject.GetComponent<Rigidbody>().isKinematic=true;
}
}
Then you should decide if once the lift reaches its destination it is the player who has to move out of the platform, or if you unparent it and let it fall down as the end of the trip
So I'm using the FPS Controller prefab from the standard asset and I've attached my script "Camera Push Up Effect" to the main camera(FirstPersonCharacter). Everything is working as it should but the problem that I'm currently having is whenever the player shoot, it doesn't push the camera upwards. What I'm trying to accomplish here is to give some recoil to the player's gun so that it does not stay in one place when the player shoots. I also want to limit the player rotation on the x-axis so that the gun doesn't keep going up forever(the min would be -90 degrees and the max would be 0).
using UnityEngine;
using InControl;
public class CameraPushUpEffect : MonoBehaviour
{
public float rotateSpeed;
public float maxRotationX;
public float minRotationX;
private Vector3 V3Rotate = Vector3.zero;
// Update is called once per frame
void Update ()
{
InputDevice device = InputManager.ActiveDevice;
if(device.RightBumper) // The player is shooting.
{
pushCameraUp();
}
else if (transform.localRotation.x != 0) // Slerp back to the original unrotated position.
{
transform.localRotation = Quaternion.Slerp(transform.localRotation, Quaternion.identity,
Time.deltaTime * rotateSpeed);
}
}
private void pushCameraUp()
{
V3Rotate.x += rotateSpeed * Time.deltaTime;
float newRotationX = Mathf.Clamp(V3Rotate.x, minRotationX, maxRotationX);
Quaternion maxRotation = Quaternion.Euler(newRotationX, 0, 0);
transform.localRotation = Quaternion.Slerp(transform.localRotation, maxRotation, Time.deltaTime * rotateSpeed);
}
}
It would appear it to be that the main culprit is the mouse look script in The First Person Controller script(This script is part of the FPSController prefab). Note the unity that I'm using has mouse look without extending MonoBehaviour and is set as private serializeable. The post in which I found my answer mentioned that it is not possible to rotate your camera(X-Axis) if the mouse look component is attached to the main camera which is clearly not the case(FPSController is the parent of the child FirstPersonCharacter which is tagged as the main camera and FirstPersonCharacter does not have mouse look attached to it). So I simply disabled the First Person Controller script to confirm if this was true and indeed it was.
Here is the link in case others would like to take a look at it as well.
Help with Recoil for guns
I have been trying to add little things on top of the tutorials Unity supplies and I am confused about how to get this certain mechanic to work.
When my player shoots it shoots in the direction of the mouse. When I run a client and host on my computer so that 2 people are connect for testing I get weird results.
I have both of my players shooting in the correct direction but when I am moving my mouse on one of the clients I see that my blue circle (which represents where the bullets will be spawned) is moving when that client is not focused, meaning I am not currently clicked on that client and when I am moving my mouse I see the blue circle moving on the client I am not focused on which I am not sure if my friend down the street was to test this would it cause errors.
Here are 2 screenshots of my Scene/Game view to get a better visual : Pic 1 - Pic 2
I ended up using Network Transform Child on my parent GameObject for one of my children GameObjects that helps spawn the location of the bullets but still the visual look from my Scene tab makes me worry about the accuracy of bullets being spawned.
Here is my shooting code that :
public class PlayerShooting : NetworkBehaviour
{
public GameObject bulletPrefab;
public GameObject fireSpot;
public float bulletSpeed;
public Transform rotater;
public Camera cam;
void Update()
{
// Only run the below code if this is the local player.
if (!isLocalPlayer)
{
return;
}
// Rotate based on the location of the mouse our spot to shoot bullets.
Vector3 dir = cam.ScreenToWorldPoint (Input.mousePosition) - rotater.position;
float angle = Mathf.Atan2 (dir.y, dir.x) * Mathf.Rad2Deg;
rotater.rotation = Quaternion.AngleAxis (angle, Vector3.forward);
// When we hit spacebar
if(Input.GetKeyDown(KeyCode.Space))
{
// Fire some bullets.
CmdFire ();
}
}
// Something the client is wanting to be done and sends this "command" to the server to be processed
[Command]
public void CmdFire ()
{
// Create the Bullet from the Bullet Prefab
var bullet = (GameObject)Instantiate (bulletPrefab, fireSpot.transform.position, Quaternion.identity);
// Add velocity to the bullet
bullet.GetComponent<Rigidbody2D>().velocity = (fireSpot.transform.position - rotater.position).normalized * bulletSpeed;
// Spawn the bullets for the Clients.
NetworkServer.Spawn (bullet);
// Destroy the bullet after 2 seconds
Destroy(bullet, 4.0f);
}
}
My movement script :
public class Movement : NetworkBehaviour {
void Update()
{
if (!isLocalPlayer)
{
return;
}
var x = Input.GetAxis("Horizontal") * Time.deltaTime * 3.0f;
var y = Input.GetAxis("Vertical") * Time.deltaTime * 3.0f;
transform.Translate(x, y, 0f);
}
}
This will continue to be a problem if you'd like to run multiple instances of unity games on the same machine. If you go into Edit > Project Settings > Player, there is a Run in Background option under Resolution and Presentation. If you have this checked, both instances of the game will receive updated mouse positions per frame from Input.mousePosition (Keep in mind Input.GetKeyDown will only register to the focused instance of the game so there's some inconsistency here). If you don't have that check box selected, your game instance will pause when not focused (I'm guessing you don't want this if you're working with a client/host networking paradigm).
There are a few ways you can work around this. The ideal way to test a networked game would be to have a unique PC for each instance of the game. If that's not an option, you can add some checks to ensure the mouse is over the window. As long as the two windows don't overlap you can do that by checking the position against the screen info:
bool IsMouseOverWindow()
{
return !(Input.mousePosition.x < 0 ||
Input.mousePosition.y < 0 ||
Input.mousePosition.x > Screen.width ||
Input.mousePosition.y > Screen.height);
}
You can then use this to decide, in your Update(), whether or not you want to update rotator.rotation.
Another option would be to implement MonoBehaviour.OnApplicationFocus. You can then enable/disable things (like updating rotations based on mousePosition) in response to that event. The cleanest solution would be to have a clear way for your systems to ask "is my window focused". You can make a class like this:
public class FocusListener : MonoBehaviour
{
public static bool isFocused = true;
void OnApplicationFocus (bool hasFocus) {
isFocused = hasFocus;
}
}
Make sure you have one of these somewhere in your game and then anything can check by looking at FocusListener.isFocused.