I'm a real noob, I am an artist that just started out programming and have looked around a lot for a solution to this, the closest i could find was a reddit post with the same problem as mine but even there no one could find a solution.
I have set up an animator controller and have given it all the parameters i need, walking, running, jumping etc. If I use a player controller script everything works like a charm. Now when I want to turn this character into an NPC and place the patrol AI script I picked up from the unity manual, things aren't working right.
I have baked my navmesh, I have added the NavMeshAgent component too. The NPC slides around, but the animation doesn't play
using UnityEngine;
using UnityEngine.AI;
using System.Collections;
public class Patrol : MonoBehaviour
{
Animator animator;
public Transform[] points;
private int destPoint = 0;
private NavMeshAgent agent;
void Start()
{
animator = GetComponent<Animator>();
agent = GetComponent<NavMeshAgent>();
// Disabling auto-braking allows for continuous movement
// between points (ie, the agent doesn't slow down as it
// approaches a destination point).
agent.autoBraking = false;
GotoNextPoint();
}
void GotoNextPoint()
{
// Returns if no points have been set up
if (points.Length == 0)
return;
// Set the agent to go to the currently selected destination.
agent.destination = points[destPoint].position;
// Choose the next point in the array as the destination,
// cycling to the start if necessary.
destPoint = (destPoint + 1) % points.Length;
}
void Update()
{
animator.SetBool("isRunning", true);
// Choose the next destination point when the agent gets
// close to the current one.
if (!agent.pathPending && agent.remainingDistance < 0.5f)
GotoNextPoint();
}
}
A screenshot of the animator
Animator starts from the default state (indicated by orange color) and transitions happen based on that. Based on the screenshot of your animator, from your default state, animator can go to Idle, SwordSlash and SwordJump states and there is no transition between the Idle state and the Run state. Nevertheless your code sets the isRunning bool to true. If you want it to work you must set the isWalking bool to true, so the animator goes into SwordWalk state. From there you can set isRunning to true to go to Run animation. Another way is to change isRunning to isWalking (By the way, I suggest you study about 1D blend trees)
Related
I am very new at C# (around 1 week in), so I have been following a lot of tutorials. I am at the moment trying to edit a piece of code (unity tutorial) to only have the enemy follow the player when in a certain range of the player (10 foot), but unfortunately without changing the code completely, I cannot find a solution. At the moment the enemy will only follow the player when the player is alive (which i also want).
I've tried googling what I want, but I don't want to change the code from what is it too much. I am very new at c# and and currently learning bit by bit, other methods I have found require me changing the code a lot. As far as I understand, the main focus of the code below is for the enemy to be completely controlled by the Nav Mesh Agent, is it possible for me to keep the same technique?
using UnityEngine;
using System.Collections;
public class EnemyMovement : MonoBehaviour
{
Transform player;
PlayerHealth playerHealth;
EnemyHealth enemyHealth;
UnityEngine.AI.NavMeshAgent nav;
void Awake()
{
// references
player = GameObject.FindGameObjectWithTag("Player").transform;
playerHealth = player.GetComponent<PlayerHealth>();
enemyHealth = GetComponent<EnemyHealth>();
nav = GetComponent<UnityEngine.AI.NavMeshAgent>();
}
void Update()
{
if (enemyHealth.currentHealth > 0 && playerHealth.currentHealth > 0)
{
nav.SetDestination(player.position);
transform.LookAt(player);
}
else
{
nav.enabled = false;
}
}
}
If possible, I would like to add 2 if functions. 1 being the check if the player is alive, and the other to check if the player is within distance. I don't know much about raycast at the moment, so will this be the next step for me to learn to get this to work how i want?
Thankyou for your time.
Dean.
You can calculate distance by doing:
float distance = Vector3.Distance(player.transform.position,transform.position);
You can do a check if it's no larger than some amount with:
bool playerIsCloseEnough = distance <= amount;
And you can check if the player is alive with:
bool playerIsAlive = playerHealth.currentHealth > 0;
Then, you can do things if they're both true with:
if (playerIsAlive && playerIsCloseEnough)
{
// ...
}
And as you mentioned in the comment, you'll need to set nav.enabled=true; in void Awake or void Start to make sure the nav mesh is turned on :)
I have created a NPC that follows the main player. When the player is in a certain range of the NPC, the NPC is supposed to walk, run, and attack based on the distance between the player and the NPC.The NPC has a C# MutantMovement Script attached, which also contains Animations. The NPC is also a NavMesh Agent. My problem is that the Animations do not run smoothly with the logic. I hope someone can help. Below is the MutantMovement Script and the Animation Controller.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
[RequireComponent (typeof (NavMeshAgent))]
[RequireComponent (typeof (Animator))]
public class MutantMovement : MonoBehaviour {
[SerializeField] private float _range = 10f;
public Transform _goal; // destination
public Transform _player;
private Animator _anim;
private bool _alive;
private float _distance;
private NavMeshAgent _agent;
void Start ()
{
_goal = GameObject.Find("Player").transform;
_player = GameObject.Find("Player").transform;
_alive = true;
// get a reference to the NavMesh Agent component
_agent = GetComponent<NavMeshAgent>();
_anim = GetComponent<Animator>();
}
void Update()
{
Vector3 direction = _goal.position - this.transform.position;
float angle = Vector3.Angle(direction, this.transform.forward);
if(Vector3.Distance(_goal.position, this.transform.position) < 10 &&
angle < 50){
_anim.SetBool("isIdle", false);
if ((_alive && direction.magnitude > 5))
{
_agent.destination = _goal.position;
_anim.SetBool("isWalking", true);
_anim.SetBool("isAttacking", false);
_anim.SetBool("isRunning", false);
if((_alive && direction.magnitude > 6.5)){
_agent.destination = _goal.position;
_anim.SetBool("isRunning", true);
_anim.SetBool("isWalking", false);
_anim.SetBool("isAttacking", false);
}
}
else{
_agent.isStopped = false;
_anim.SetBool("isAttacking", true);
_anim.SetBool("isWalking", false);
_anim.SetBool("isRunning", false);
}
}else{
_anim.SetBool("isIdle", true);
_anim.SetBool("isWalking", false);
_anim.SetBool("isAttacking", false);
}
}
public void SetAlive(bool alive)
{
_alive = alive;
}
}
Animation Controller
I would use Animator triggers rather than booleans in your particular case, so the transitions from one animation to another are triggered only once.
In your state machine, you can then set the transitions so :
walking or attacking ---[trigger run]----> running animation
running or walking -----[trigger attack]---> attack animation
running or attacking ----[trigger walk] ---> walking animation
It makes it overall easier as you don't have to turn the other bools to false when triggering a new animation state.
Another thing you can check to make sure the transitions are fluid, is to have a look at the exit time of your animations - if you turn "Has Exit Time" off, your transitions will be instant. You can find this option if you open your animator controller and click on your state machine transition arrow.
The first wave of green goes right (to the first waypoint), but after lengthening the tunnel, the second wave is why green you lose the first waypoint and go straight to the second. (And why is that somehow a roundabout way)
Sorry for my bad english.
The first wave of green goes right (to the first waypoint), but after lengthening the tunnel, the second wave is why green you lose the first waypoint and go straight to the second. (And why is that somehow a roundabout way)
Actually two questions:
1) how to fix the first waypoint
2) why is it so weird going to the second waypoint
Here is the code of the enemy to iterate through waypoints.
using System;
using UnityEngine;
using UnityEngine.AI;
[RequireComponent(typeof(NavMeshAgent))]
public class EnemyMovement : MonoBehaviour
{
[SerializeField] public Transform[] points;
[SerializeField] private int destPoint = 0;
private NavMeshAgent agent;
void Start()
{
agent = GetComponent<NavMeshAgent>();
agent.autoBraking = false;
agent.destination = points[destPoint].position;
}
void GotoNextPoint()
{
if(destPoint != points.Length)
{
agent.destination = points[destPoint].position;
}
}
void Update()
{
if(agent.remainingDistance < 0.5f)
{
destPoint++;
GotoNextPoint();
}
}
private void OnDrawGizmos()
{
Gizmos.DrawLine(gameObject.transform.position, points[destPoint].position);
}
}
It’s decided that the thing is that NavMesh makes large tiles (NavMeshBuildSettings.tileSize), but I couldn’t change it because I used someone else’s work (https://github.com/Unity-Technologies/NavMeshComponents/tree/mast… mples / Scripts). So it turned out that to change the runtime navMesh, you must not only register in the grid change code, but write the line (.overrideTileSize = true;)
var defaultBuildSettings = NavMesh.GetSettingsByID (0).overrideTileSize = true;
After that I was able to change the size of the tile, and the choice of the wrong path was stopped.
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 created a new controller called it SoldierController and dragged it to the character Controller under Animator component in the Inspector.
Also unchecked Apply Root Motion
Then attached a new script to the third person character called the script Soldier.
Then i set the animator controller i added to it two new States: Walk and Idle.
HumanoidIdle and HumanoidWalk.
Then i did that the default start state will be Idle. Set StateMachine Default State.
Then i did a Transition from Walk to Idle. This way when i press on W it's start walking a bit then it keep moving the character but without the walking animation.
If i delete this transition and make transition from the Idle to Walk then when i press on W it will walk but then if i leave W key and it's idling then after 2-3 seconds the character will walk on place i mean it will animate walking without moving and i'm not pressing anything it's starting automatic when idling.
The character had another animator controller but i created a new one and using only the new one.
And the script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Soldier : MonoBehaviour
{
private bool _isWalking = false;
private Animator anim;
// Use this for initialization
void Start ()
{
anim = GetComponent<Animator>();
}
// Update is called once per frame
void Update ()
{
var x = Input.GetAxis("Horizontal") * Time.deltaTime * 150.0f;
transform.Rotate(0, x, 0);
if (Input.GetKey("w"))
{
if (!_isWalking)
{
_isWalking = true;
anim.Play("Walk");
}
var z = Input.GetAxis("Vertical") * Time.deltaTime * 3.0f;
transform.Translate(0, 0, z); // Only move when "w" is pressed.
}
else
{
if (_isWalking)
{
anim.Play("Idle");
}
_isWalking = false;
}
}
}
First of all, you are mixing between 2 methods. You are playing the animations from the code and also assigning trasitions in animator. They will conflict. You either do the whole animation control from code or follow these steps:
1. Make a finite state machine in the animator window using transitions.
2. Add parameters to those transitions(bool, int etc)
3. Change the value of the parameters from code like anim.SetBool("walk", true) to control animations.
Another thing, don't forget to set the idle or walk animation's wrap mode to loop. Otherwise, they'll play once and then stop.