In my game I want to have one of my enemies move towards the player however they don't do that for some reason. The enemy is supposed to look at the player and when the player enters the range they should start moving towards him. My problem is that they aren't doing anything. They just stand there. Also they don't even fall when they have a rigidbody. It currently has an Animator, box collider, and a capsule collider.
Edit: I forgot to add this but the script also triggers the animations
Edit #2: Also I know that it isn't because the movement is in the if statement
(Sorry if it is bad I am a programmer noob)
This is the script responsible for the players movement:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Mar_Tracker : MonoBehaviour
{
public Transform Player;
public float MoveSpeed = 3.0f;
public float InRadius = 250.0f;
public float AttackRange = 11.0f;
public float rocketRange = 50.0f;
private Animator anim;
private Coroutine RocketLouch = null;
public GameObject Rocket;
public GameObject Explosion;
public SphereCollider sphereCollider;
private void Start()
{
anim = GetComponent<Animator>();
Player = GameObject.FindGameObjectsWithTag("Player")[0].transform;
sphereCollider.enabled = false;
}
void Update()
{
Player = GameObject.FindGameObjectsWithTag("Player")[0].transform;
transform.LookAt(Player); // Makes it so that the enemy looks at player
float dstSqr = (Player.position - transform.position).sqrMagnitude;
bool inRadius = (dstSqr <= InRadius * InRadius);
bool inAttackRange = (dstSqr <= AttackRange * AttackRange);
bool inRocketRange = (dstSqr <= rocketRange * rocketRange);
anim.SetBool("inArea", inRadius);
anim.SetBool("Attacking", inAttackRange);
if (inRadius)
{
transform.position += transform.forward * MoveSpeed * Time.deltaTime; // (movement)
}
if (inRocketRange)
{
if (RocketLouch == null)
{
RocketLouch = StartCoroutine(RocketLaunch());
}
}
}
IEnumerator RocketLaunch()
{
anim.SetBool("Rocket", true);
yield return new WaitForSeconds(0.15f);
sphereCollider.enabled = true;
Explosion.SetActive(true);
yield return new WaitForSeconds(1.0f);
anim.SetBool("Rocket", false);
Destroy(Rocket);
}
}
Your using math that may be a tad to complicated for what you want to achieve and I think the issue is happening there.
Lets simplify it
Transform Player;
float InRadius;
float AttackRange;
float rocketRange;
void Start()
{
// Set it at the start to optimize performance
Player = GameObject.FindGameObjectsWithTag("Player")[0].transform;
}
// Use fixed Update for moving things around
// Better performance
void FixedUpdate()
{
HandlePlayerDetection() ;
}
void HandlePlayerDetection()
{
transform.LookAt(Player); // Makes it so that the enemy looks at player
// Find the distance between the player and the transform
var distance = Vector3.Distance(transform.position, player.position);
// Do the boolean calculations
bool inRadius = distance <= InRadius;
bool inAttackRange = distance <= AttackRange;
bool inRocketRange = distance <= rocketRange;
// Lets use inbuilt functions to movement
if (inRadius)
{
transform.position = Vector3.MoveTowards(transform.position, Player.position, MoveSpeed * Time.deltaTime);
}
// Rocket code
if (inRocketRange)
{
FireRocket();
}
}
void FireRocket()
{
if (RocketLouch == null)
{
RocketLouch = StartCoroutine(RocketLaunch());
}
}
Now if you really want your enemy to walk properly I would recommend watching a tutorial on Nav Meshes. It is super easy to use and will allow the enemy to walk around objects and do propper path finding.
Related
Context
So I'm making a clone of Crossy Roads where the camera follows the player. It does some linear interpolation (Lerp) after moving, and the camera starts moving away from the player in the positive direction (x-axis until camera reaches to a certain range where player is not visible enough). Things I have tried is by flagging it, but I think I'm doing it wrong.
Problem
I have done my camera movements accordingly, but I am having an issue where the conditions are not properly met. I'm get the offset camera after not moving, but it does not the Lerp, and vice-versa. I want both to happen after a certain condition after the game starts. When the player moves, the camera follows it in Lerp. However, once the player is "Idle", its still Lerping. I want the camera to continue by itself and at the same time focus at the player's object.
Example
Camera with Lerp, but not moving away from the player
Camera moving away, but not following player with lerp
Code
CameraController.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/*
** Camera following player (Smoothing and angle): https://youtu.be/4HpC--2iowE
** Maximo: https://www.youtube.com/watch?v=bXNFxQpp2qk&ab_channel=iHeartGameDev
** Moving character relative to camera: https://forum.unity.com/threads/moving-character-relative-to-camera.383086/
** Camera follow v2: https://youtu.be/Jpqt2gRHXtc?list=PLq_nO-RwB516fNlRBce0GbtJSfysAjOgU
*/
public class CameraController : MonoBehaviour
{
public GameObject player;
public PlayerControl playerControlScript;
private Vector3 newCameraPos;
public bool stillIdle;
void Start()
{
stillIdle = false;
PlayerControl playerControlScript = GetComponent<PlayerControl>();
}
void LateUpdate()
{
player = GameObject.FindGameObjectWithTag("Player");
if (playerControlScript.GetfirstInput()) //True
{
stillIdle = true;
newCameraPos = Vector3.Lerp(transform.position, playerControlScript.transform.position, Time.deltaTime);
transform.position = new Vector3(newCameraPos.x, 1, newCameraPos.z);
}
if (stillIdle)
transform.position = new Vector3(transform.position.x + 0.69f * Time.deltaTime, transform.position.y, transform.position.z); //Moving camera away effect
}
}
PlayerControl.cs
public class PlayerControl : MonoBehaviour
{
bool firstInput;
Vector3 startPos;
Vector3 endPos;
public bool GetfirstInput() //I was learning how to have a Get function while my member was private from another script file
{
return firstInput;
}
void Update()
{
if (Input.GetButtonDown("up") || Input.GetButtonDown("left") || Input.GetButtonDown("right") || Input.GetButtonDown("down"))
{
//if game starts
{
//Other variables being initialized here
firstInput = true;
}
}
}
}
Hierarchy/Inspector
Main Camera
Player Object
Some help would be appreciate it. I feel I have been staring at this problem and I bet it is something minimal and small from just thinking it.
Let me know if you need clarifications. I'm happy to edit and answer them for everyone
If player in your game does not change, you don't have to find the player reference in each LateUpdate().
I notice that once stillIdle is set true, it never goes back to false, is this your intention to do that?
You call Lerp when playerControlScript.GetfirstInput() is true, so maybe we need to look at its implementation. Maybe it turns true in some conditions you do not intend it to.
Maybe Try this
public class PlayerControl : MonoBehaviour
{
private bool _hasFireFirstInput = false;
private bool _isIdle = true;
public bool IsIdle => _isIdle;
public bool HasFireFirstInput => _hasFireFirstInput;
private void Update()
{
if (Input.GetButton("Horizontal") || Input.GetButton("Vertical"))
{
_hasFireFirstInput = true;
_isIdle = false;
Vector3 pos = transform.position;
pos = new Vector3(pos.x, pos.y, pos.z + .2f * Time.deltaTime);
transform.position = pos;
}
else
{
_isIdle = true;
}
}
}
I use Input.GetButton() rather than Input.GetButtonDown(), since the later only return true at the frame the button is pressed, meaning that if i long-press the button, it will return false after the next frame.
public class CameraController : MonoBehaviour
{
[SerializeField] PlayerControl _playerControlScript;
[SerializeField] Vector3 _offset;
[SerializeField] float _lerpSpeed;
bool _iskeepLerping = false;
float _lerpVal = 0f;
private void LateUpdate()
{
if (!_playerControlScript.HasFireFirstInput)
{
return;
}
if (_playerControlScript.IsIdle)
{
MoveAway();
}
else
{
Lerp();
}
}
private void MoveAway()
{
_iskeepLerping = false;
transform.position = new Vector3(transform.position.x + 0.69f * Time.deltaTime, transform.position.y, transform.position.z);
}
private void Lerp()
{
if (!_iskeepLerping)
{
_lerpVal = 0f;
}
Vector3 newCameraPos = Vector3.Lerp(transform.position, _playerControlScript.transform.position + _offset, _lerpVal);
transform.position = newCameraPos;
_lerpVal += _lerpSpeed * Time.deltaTime;
_iskeepLerping = true;
}
}
I'm making a 2D game where the player moves left and right while dodging falling objects, I put triggers so it doesn't leave the screen but sometimes when it collides with these triggers it passes this collider. I sent an image of the colliders, the Player inspector and also a collider.
Here my Player code
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Ball : MonoBehaviour
{
public bool isRight;
public float speed;
public Transform pointR;
public Transform pointL;
Rigidbody2D rb;
private SpriteRenderer sprite;
private float maxSpeed;
[Header("PowerUps")]
public float force;
// Start is called before the first frame update
void Start()
{
rb = GetComponent<Rigidbody2D>();
sprite = GetComponent<SpriteRenderer>();
maxSpeed = 600f;
}
void Update()
{
if(Input.GetMouseButtonDown(0))
{
isRight = !isRight;
}
}
void FixedUpdate()
{
if(isRight)
{
rb.velocity = new Vector2(speed, 0);
rb.AddForce(rb.velocity,ForceMode2D.Force );
sprite.flipX = true;
if(rb.velocity.x >= maxSpeed)
{
rb.velocity = new Vector2(maxSpeed, 0);
}
}
else
{
rb.velocity = new Vector2(-speed, 0 );
rb.AddForce(rb.velocity, ForceMode2D.Force );
sprite.flipX = false;
}
}
void OnTriggerEnter2D(Collider2D collider)
{
if(collider.gameObject.tag =="ColliderL")
{
isRight = !isRight;
}
if(collider.gameObject.tag == "ColliderR")
{
isRight = !isRight;
}
// For PowerUps
if(collider.gameObject.tag == "SpeedUp")
{
StartCoroutine(Seconds());
}
}
IEnumerator Seconds()
{
rb.AddForce(rb.velocity * force);
yield return new WaitForSeconds(5f);
rb.AddForce(rb.velocity * -force);
}
}
I would ideally need to see your scene setup to further pinpoint your mistakes. But these are a few common mistakes that are made:
Tags are case-sensitive. Also, make sure to assign them.
Your collider on one of the object is not 2D; There is Collider, then there is Collider2D.
Your collider is not marked as trigger.
Though there are a few non-related problems with your setup:
rb.velocity.collider doesn't exist. You are looking for rb.velocity.
Ideally you want to set physics changes (velocity, in your case) to FixedUpdate() instead.
If you do want to get colliders from RigidBody, use GetAttachedColliders.
Assuming you are setting up a border which the player can never pass through, you should use a full collider instead of a trigger. This uses OnCollisionEnter2D.
new Vector2(speed, 0* Time.deltaTime);
0 * Time.deltaTime does nothing, since you are multiplying by 0.
What you are looking for is probably new Vector2(speed * Time.deltaTime, 0);. But you don't need to multiply the speed by deltaTime since you are not moving the ball manually via it's Transform.
rb.velocity = new Vector2(speed, 0); should be your result.
Im throwing a bomb using physics made by code. For some reason it doesnt detect collision unless my physics stop applying force to the object. in order to bypass it I cancelled the applied force and applied gravity to it on collision enter and put the movement of the bomb to LateUpdate so it will trigger after the OnCollisionEnter but the bomb collides only most of the time with the floor (mesh collision, the floor made with ProBuilder) and not all of the time. the bomb collision detection is set to continuous
Will appreciate all the help, Thanks!
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BombBehavior : MonoBehaviour
{
[SerializeField] float ExplosionForce = 300;
[SerializeField] float ExplosionRadius;
[SerializeField] float LaunchForceX;
[SerializeField] float LaunchForceY;
[SerializeField] float Delay;
float countdown;
Rigidbody rigidbodyy;
float gravity = 1;
Player player;
bool HasExploded = false;
// Start is called before the first frame update
void Start()
{
rigidbodyy = GetComponent<Rigidbody>();
player = Player.p;
GetLaunchForceX();
countdown = Delay;
}
private void GetLaunchForceX()
{
if (transform.position.x > player.transform.position.x)
{
LaunchForceX *= 1;
}
else if (transform.position.x < player.transform.position.x)
{
LaunchForceX *= -1;
}
}
private void LateUpdate()
{
ThrowBomb();
}
private void Update()
{
countdown -= Time.deltaTime;
if (countdown <= 0 && !HasExploded)
{
ExplodeNearEnemy();
}
}
private void ThrowBomb()
{
if (rigidbodyy.useGravity == false)
{
Vector3 ThrowDirection = new Vector3(LaunchForceX, LaunchForceY, 0);
LaunchForceY -= gravity;
ThrowDirection.y = LaunchForceY;
transform.Translate(ThrowDirection * Time.deltaTime);
}
}
private void ExplodeNearEnemy()
{
Collider[] colliders = Physics.OverlapSphere(transform.position, ExplosionRadius);
foreach (Collider collider in colliders)
{
if (collider.gameObject.CompareTag("Enemy"))
{
Rigidbody enemyRB = collider.GetComponent<Rigidbody>();
if (enemyRB != null)
{
enemyRB.useGravity = true;
enemyRB.AddExplosionForce(ExplosionForce, transform.position, ExplosionRadius);
Destroy(enemyRB.gameObject,1);
}
}
}
//Destroy(gameObject);
}
private void OnCollisionEnter(Collision collision)
{
LaunchForceY = 0;
LaunchForceX = 0;
gravity = 0;
rigidbodyy.useGravity = true;
}
}
I don't exactly understand the problem but it looks like you want to make a Bomb/Granate so why dont you just write a function and use AddForce()?
void Thow(Vector3 direction, float strength)
{
rigidbodyy.AddForce(direction * strength, ForceMode.Impulse);
}
something like this should help as you only need to get the throwing direction and apply a strength then Unity will handle the rest
Or maybe if your bomb doesn't collide with the ground give the ground a rigidbody and set kinematic to true
Use the MovePosition() to move a rigid body if you want colliders etc to work. Teleporting a rigid body by altering the transform directly messes up the physics.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Playables;
public class SpaceshipCutscene : MonoBehaviour
{
public Transform player;
public Transform[] npcs;
public Transform console;
public Camera FPSCamera;
public Camera mainCamera;
public Animator[] anim;
public float rotationSpeed = 3f;
public float distanceFromConsole;
private bool moveNpc = false;
private float sp = 0f;
private float distance;
// Use this for initialization
void Start()
{
}
private void Update()
{
distance = Vector3.Distance(transform.position, npcs[0].transform.position);
if (moveNpc)
{
// Soldier 2 rotating and looking at player
Vector3 dir = player.position - npcs[0].position;
dir.y = 0; // keep the direction strictly horizontal
Quaternion rot = Quaternion.LookRotation(dir);
// slerp to the desired rotation over time
npcs[0].rotation = Quaternion.Slerp(npcs[0].rotation, rot, rotationSpeed * Time.deltaTime);
var dist = Vector3.Distance(npcs[1].position, console.position);
if (dist < distanceFromConsole)
{
sp += Time.deltaTime;
sp = Mathf.Clamp(sp, 0f, 1f);
anim[1].SetFloat("WalkingSpeed", sp);
}
Vector3 dirToComputer = console.transform.position - npcs[1].position;
dirToComputer.y = 0;
Quaternion rot1 = Quaternion.LookRotation(dirToComputer);
npcs[1].rotation = Quaternion.Slerp(npcs[1].rotation, rot1, rotationSpeed * Time.deltaTime);
}
}
private void OnTriggerExit(Collider other)
{
if (HoriDoorManager.doorLockState == false && distance < 5f)
{
if (other.gameObject.tag == "SpaceshipCutscene")
{
FPSCamera.enabled = false;
mainCamera.enabled = true;
moveNpc = true;
anim[0].SetBool("Aiming", true);
anim[1].SetBool("Walktouse", true);
}
}
}
}
I'm calculating the distance between the player and the first npcs since I want the OnTriggerExit to trigger and work on specific direction.
When using a break point on the line:
FPSCamera.enabled = false;
It stop on this line and then I hit continue and it's getting inside the rest of the code in the Update.
But if I'm not adding a break point on that line it's not working it does nothing. Not giving errors or exception just not getting to the rest of the code in the Update.
https://docs.unity3d.com/ScriptReference/Collider.OnTriggerExit.html
OnTriggerExit occurs on the FixedUpdate after the Colliders have stopped touching
Update happens every frame, while FixedUpdate happens on a timer. So things here is probably just out of sync. Creating a break point will ensure that the frame stops and everything is probably aligned when you do that.
Hey I am making a top down game in unity. The problem I ma having is making npc players change the way they are facing while following the player. So if the player turns left the npc follows them but doesn't turn to face the direction the npc is going. I can get the npc to look like its walking just not change the direction it is looking. This is a 2d top down game please any help will be nice. here is my npc code.
using UnityEngine;
using System.Collections;
public class SlimeController : MonoBehaviour
{
public Transform Character; // Target Object to follow
public float speed = 0.1F; // Enemy speed
public float maxDist = 10.0f;
public float attackdistance = 3;
public float farenough;
private Vector3 directionOfCharacter;
private bool challenged = false;// If the enemy is Challenged to follow by the player
public Transform StartMarker;
private Vector3 goback;
public Transform EndMarker;
public Rigidbody2D rb;
Animator anim;
float oldx;
bool left;
bool right;
private void Start()
{
rb = GetComponent<Rigidbody2D>();
anim= GetComponent<Animator>();
oldx = transform.position.x;
}
void Update()
{
anim.SetBool("left", false);
anim.SetBool("right", false);
var distanceFromPlayer = Vector3.Distance(Character.position, transform.position);
if(oldx>transform.position.x)
{
left = false;
right = true;
}
if(oldx<transform.position.x)
{
left = true;
right = false;
}
if (oldx == transform.position.x)
{
left = false;
right = false;
}
if (challenged)
{
directionOfCharacter = Character.transform.position - transform.position;
directionOfCharacter = directionOfCharacter.normalized; // Get Direction to Move Towardsss
transform.Translate(directionOfCharacter * speed, Space.World);
enabled = true;
if (distanceFromPlayer < attackdistance)
{
attack();
}
if (distanceFromPlayer > attackdistance)
{
speed = 0.03f;
}
}
if (!challenged)
{
goback = StartMarker.transform.position - transform.position;
goback = goback.normalized;
transform.Translate(goback * speed, Space.World);
}
}
// Will be triggered as soon as player would touch the Enemy Object
void OnTriggerEnter2D(Collider2D other)
{
if (other.gameObject.name == ("Player"))
{
challenged = true;
}
}
private void OnTriggerExit2D(Collider2D other)
{
if (other.gameObject.name == ("Player"))
{
speed = 0.03f;
challenged = false;
}
}
void attack()
{
speed = 0;
transform.Translate(directionOfCharacter * speed, Space.World);
}
}
This is because you are just moving the object towards your target. But to have it look at your target you need to also rotate it in the direction of your target.
The Transform Component has a function called LookAt. You supply it with your Target and the Axis your object should rotate around. So in your case:
this.transform.LookAt(Character, Vector3.up);
See here for more info on LookAt.