Make enemy follow you when within a certain distance - c#

I have enemies move along a set path (way points) but I want an enemy to follow the player when you are within a certain distance of it but then when out of that distance, the enemy goes back to its way points and continues moving along the path until the player is within a certain distance again. So it keeps doing that. Below is the code I got for the enemies to move along the way points.
I am using Unity 2d and C#
[SerializeField]
Transform[] waypoints; //EnemyWayPoint;
[SerializeField]
float moveSpeed = 3f;
int waypointIndex = 0;
private void Start()
{
transform.position = waypoints[waypointIndex].transform.position;
}
private void Update()
{
Move();
}
private void Move()
{
transform.position = Vector3.MoveTowards(transform.position, waypoints[waypointIndex].transform.position, moveSpeed * Time.deltaTime);
if (transform.position == waypoints[waypointIndex].transform.position) {
waypointIndex += 1;
}
if (waypointIndex == waypoints.Length)
waypointIndex = 0;
}
}

Check for the distance between enemy and player using Vector3.Distance(otherPos,transformPos)
and made check for distance according to your choice if it's less than distVal, then enemy should follow or action performed
var distVal=5.0f;
var dis=Vector3.Distance(enemyTransform.position,playerTransform.position);
if(dis<=disVal)
{
//what enemy should do.
}
Next is your own choice for normalization of vectors you set while read for distance document here

Arslan's answer is fine -
just adding a slight change to the code so you know it can be done this way too...
var distVal=5.0f;
if (Vector3.Distance(enemyTransform.position, playerTransform.position) < disVal)
{
Debug.Log("Enemy following you");
}
It's much the same but good to know of other ways it exists.

Related

Camera relative moment

I'm new to Unity 3D and I've started to study and learn player and camera mechanics these past few weeks.Although, I have a simple character controller system with a Cinemachine free look cam following the player, I need help incorporating a camera relative player movement mechanic into my project. I therefore need help incorporating such a mechanism into my system. I've added my PlayerController Code below
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
[SerializeField] public float _playerSpeed;
[SerializeField] public Rigidbody rb;
[SerializeField] private float _jumpforce = 100 ;
[SerializeField] private float fallMultiplier;
[SerializeField] private float lowJumpMultiplier;
public bool isGrounded;
public bool jumpReady;
public float jumpcoolDownTimer = 1.5f;
public float jumpcoolDownCurrent = 0.0f;
[SerializeField] private float sensi;
private float rotX;
private float rotY;
private Vector3 rotate;
int isJumpingHash;
int isFallingHash;
int isLandingHash;
public Animator animator;
Vector3 vel;
// Start is called before the first frame update
void Start()
{
jumpcoolDownCurrent = jumpcoolDownTimer;
Cursor.visible = false;
Cursor.lockState = CursorLockMode.Locked;
animator = GetComponent<Animator>();
//Converting string values to hash values to save memory
isJumpingHash = Animator.StringToHash("isJumping");
isFallingHash = Animator.StringToHash("isFalling");
isLandingHash = Animator.StringToHash("isGrounded");
var cam = Camera.main;
}
// Update is called once per frame
void Update()
{
Move();
Jump();
}
private void OnCollisionEnter(Collision collision){
if(collision.gameObject.tag == "Surface"){
isGrounded = true;
}
}
private void Move()
{
vel = new Vector3(Input.GetAxis("Horizontal"),0,Input.GetAxis("Vertical")) * _playerSpeed;
if(Input.GetKey(KeyCode.LeftShift) && Input.GetKey(KeyCode.W)){
transform.position += transform.forward * Time.deltaTime * _playerSpeed;
}
}
private void Jump(){
vel.y = rb.velocity.y;
rb.velocity = vel;
//Jump Cooldown timer
if(jumpcoolDownCurrent >= jumpcoolDownTimer){
jumpReady = true;
}
else{
jumpcoolDownCurrent += Time.deltaTime;
jumpReady = false;
}
bool jump = animator.GetBool(isJumpingHash);
bool fall = animator.GetBool(isFallingHash);
bool land = animator.GetBool(isLandingHash);
//Jump
if(Input.GetKeyDown(KeyCode.Space) && isGrounded && jumpReady){
rb.AddForce(Vector3.up * _jumpforce, ForceMode.Impulse);
isGrounded = false;
jumpcoolDownCurrent = 0.0f;
animator.SetBool(isJumpingHash, true);
jump = true;
}
//Fall
if((rb.velocity.y <= 0 && !isGrounded)){
rb.velocity += Vector3.up * Physics.gravity.y * (fallMultiplier - 1) * Time.deltaTime;
animator.SetBool(isFallingHash, true);
fall = true;
}
//Land
if(isGrounded && fall){
fall = false;
animator.SetBool(isFallingHash, false);
animator.SetBool(isLandingHash, true);
}
//Back to 2d movement
if(isGrounded && rb.velocity.y <= 0){
animator.SetBool(isLandingHash, false);
land = false;
animator.SetBool(isJumpingHash, false);
jump = false;
animator.SetBool(isFallingHash, false);
fall = false;
}
}
}
I've referred to several different YouTube tutorials and and also surfed different forums to find a solution, but to no avail. But I did notice that all these users were using the Quaternion Rotation mechanics and cam transforms. Upon attempting to incorporate these codes into my project, the camera continuously rotates as I try to rotate the camera in one particular direction, or the A and D (strafe left and right) animations weren't functioning properly.
P.S -> For those of you who don't know, camera relative moment is the mechanic that most third person games use now, where the player while in movement, turns along the rotation and the camera and runs in the direction the camera is facing. Good examples of that would be God of War (2018), Batman Arkham Series etc.
I highly recommend that you don't use CineMachine, I have never seen anyone able to use it successfully and it is very simple to make your own camera controller after you study for a few days (or pay for someone to make one for you).
To make Camera Relative Movement in a platformer game similar to God of War, you should look into transform.Rotation, EulerAngles and Quaternions (Penny De Byl has good online classes and books on dealing with Quaternions, they're Vector4 and very difficult to wrap your head around)
Then, throughout your game, add a bunch of walls with a trigger collider attached that rotate the camera to 1 angle or another when the player crosses them.
You might even want to make the camera a child of the player object then it will always have the perfect angle and move when the player rotates.
If you ask a more specific question later, then a more specific answer can be given.
Using Camera.main.transform.forward you can get the forward direction relative to the camera. You can use Camera.main.transform.right to get the right direction. If you set the y coordinate of these to zero, you can use these to define the direction you travel in. You would then need to normalize it, and multiply it by your input.
So, here is what I would do:
get forward direction for camera
scale it so that the vertical component is zero
normalize it so that you get a normalized directional vector
multiply by your vertical movement to get your vector for movement forward and back
do the same for right and left movement
add the 2 parts together
multiply by speed to get your velocity
Like this:
vel = (Vector3.Scale(Camera.main.transform.forward, new Vector3(1, 0, 1)).normalized * Input.GetAxis("Vertical"))
+ (Vector3.Scale(Camera.main.transform.right, new Vector3(1, 0, 1)).normalized * Input.GetAxis("Horizontal")))
* playerSpeed;
Also, while it is not relevant to your issue at the moment, it is best to normalize your input vector. So do something like: Vector3 inputDirection = new Vector3(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical"), 0f).normalized. Otherwise, diagonal movement ends up being much faster than movement in one direction.

Unity2D move object up and back down with smooth transition

(I'm a beginner so please have patience with me).
How it needs to happend:
I have a player that can jump and hit a tile, on collision the tile with move a fixed distance up and come back down with a smooth transition.
What I have so far:
I am detecting the collision now there's only the matter of moving the tile up and down.
The catch
The tiles are suspended in air so basically they either don't have a RigidBody2D or they have one with gravity scale 0
What I've tried
Basically I've tried 2 solutions:
(I'm not limited to these 2, I want to implement the solution that is correct so I am open to other ideas)
I was thinking to simply take the current position, calculate another vector with another position, and lerp to the new position and then lerp back to the initial one.
Vector2 initialPosition = transform.position;
Vector2 targetPosition = new Vector2(initialPosition.x + 4f, initialPosition.y + 4f);
print("Initial position: " + initialPosition);
print("Target position: " + targetPosition);
Vector2.Lerp(initialPosition, targetPosition, 1f);
I tried adding a rigidbody with scale 0
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.tag == "Ground")
{
Jumping = false;
anim.SetInteger("State", 0);
}
// print("Colision layer: " + collision.collider.gameObject.layer);
if (collision.collider.gameObject.layer == Mathf.Log(layerMask.value, 2))
{
GameObject Tile = collision.gameObject;
Rigidbody2D rigidBody = Tile.GetComponent<Rigidbody2D>();
rigidBody.gravityScale = 1;
rigidBody.AddForce(new Vector2(0, 10f), ForceMode2D.Force);
StartCoroutine(MoveTileWithForce(rigidBody));
}
}
IEnumerator MoveTileWithForce(Rigidbody2D TileRigidBody)
{
yield return new WaitForSeconds(1);
TileRigidBody.AddForce(new Vector2(0, -5f), ForceMode2D.Force);
TileRigidBody.gravityScale = 0;
print("END MY COROUTINE, gravityScale: " + TileRigidBody.gravityScale);
}
I think the best way you can achieve this is in this simple way:
1
Make sure you have a box collider for your character and a Rigidbody and the same for the block you wanna move. For both of the colliders set on trigger. If you want to have another collider for the player in order to make him touch the ground or hit enemies you can add another box collider, but make sure you have one on trigger on his head
2
Add a script to the block you wanna move and also a tag to the player, for example "player"
3
Inside of this new script check if the block is triggering the player:
void OnTriggerEnter2D(Collision other)
{
if(other.compareTag("player"))
{ StartCoroutine(MovingBlock(0.5f, transform.position, upperPosition));}
|
It will enter inside the if when the player touches the block, it will start a coroutine that I will now write so you will understand the 0.5f and the other variables
IEnumerator MovingBlock(float time, Vector2 startpos, Vector2 endpos)
{
float elapsedTime = 0;
while (elapsedTime < time)
{
transform.position= Vector2.Lerp(startpos, endpos, (elapsedTime / time));
elapsedTime += Time.deltaTime;
yield return null;
}
elapsedTime = 0f;
while (elapsedTime < time)
{
transform.position= Vector2.Lerp(endpos, startpos, (elapsedTime / time));
elapsedTime += Time.deltaTime;
yield return null;
}
}
Basically, the Coroutine will move the object from startpos to endpos and then back again in the amount of time that you decide (in this case 0.5 seconds). I have used the Vector2 variable upperposition and it's just the height you want to reach with the platform.
If you want, you can also add inside the coroutine some yield return new WaitForSeconds(timeToWait) and make the platform wait in a certain position the amount of seconds you want (timeToWait)
Declare Vector2 initialPosition at the beginning of your class.
On Start() of the tile object you should get the initialPosition = transform.position.
And in the OnCollisionEnter2D(Collision2D collision) you could start a coroutine to bring the tile back down.
So for example:
Player Script:
public class Player : MonoBehaviour
{
public Rigidbody2D rb;
//Initialize the rigidbody on the editor.
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.Space))
{
rb.AddForce(new Vector2(0f, 1000f));
}
}
}
And the tile script:
public class MyTile : MonoBehaviour
{
Vector2 initialPosition;
float speed = 2f; //the speed the tile will go down.
private void Start()
{
initialPosition = transform.position;
}
private void OnCollisionEnter2D(Collision2D collision)
{
//The amount you want to go up on the Y component, for this example I used 2.
transform.position = new Vector2(transform.position.x, transform.position.y + 2f);
StartCoroutine(MoveTileDown());
}
IEnumerator MoveTileDown()
{
while (transform.position.y > initialPosition.y)
{
transform.position = Vector2.Lerp(transform.position, initialPosition, Time.deltaTime * speed);
yield return null; //Make it run every frame, just like Update()
}
StopCoroutine(MoveTileDown());
}
}
There are a lot of ways you can achieve the same result, this is just one of them.

Moving rigidbody

I am new to unity and I have a object that I want to move.
But this object doesn't move at all. It changes his direction so the object can look to the left/right but it doesn't move from the point he starts.
So basicly the object can rotate but not move in any direction.
The question is: How can I make the object move
public float movementSpeed = 20;
Animator anim;
Rigidbody rb;
public Text countText;
public Text winText;
private int count;
void Start()
{
anim = GetComponent<Animator>();
rb = GetComponent<Rigidbody>();
count = 0;
SetCountText();
winText.text = "";
}
void FixedUpdate()
{
ControllPlayer();
}
void ControllPlayer()
{
float moveHorizontal = Input.GetAxisRaw("Horizontal");
float moveVertical = Input.GetAxisRaw("Vertical");
Vector3 movement = new Vector3(moveHorizontal, 0.0f, moveVertical);
if (movement != Vector3.zero)
{
transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(movement), 0.15f);
anim.SetInteger("Walk", 1);
}
else
{
anim.SetInteger("Walk", 0);
}
rb.addForce(movement * movementSpeed * Time.deltaTime);
}
public void OnTriggerEnter(Collider other)
{
if (other.gameObject.CompareTag("Pick Up"))
{
other.gameObject.SetActive(false);
count = count + 1;
SetCountText();
}
}
void SetCountText()
{
countText.text = "Food: " + count.ToString();
if (count >= 10)
{
SceneManager.LoadScene("Victory");
}
}
This seems to be a little overcomplicated for what you need to do.
I don't really do stuff in the 3D world at the moment, but a quick YouTube search brought up this video.
The code he used was
transform.Translate(moveSpeed*Input.GetAxis("Horizontal")*Time.deltaTime,0f,moveSpeed*Input.GetAxis("Vertical")*Time.deltaTime);
inside the Update() method, with moveSpeed being a public variable so you can change it in the inspector.
You could then do a check to see if you're moving and set the animation values accordingly.
I would always recommend putting player movement inside the Update()method as otherwise it can feel laggy and miss key presses, especially if you have a lot going on - eg a double jump.
Hope this helps, and good luck with Unity :).
By the way, as you're new I would already recommend searching Youtube first for a tutorial - most of the time someone has done one on what you want to do. Creators like Brackeys have a massive backlog of stuff which is really useful.

getting my 2d sprite flip to work depending on a Transform's location from it

So, this code just isn't responding for whatever reason. I have an enemy that I'm trying to get to face the player at all times (The enemy swoops over the player's head back and forth periodically). But other than making a flip happen whenever a timer hits 0, which doesn't really work all that well, I can't get the Flip function to work. I know the Flipper function is fine; I already tested it out and everything. I'm just not sure how to tell the enemy that when the player is to the left of it, to turn, and vice versa.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class dragoonDetection : MonoBehaviour {
private Rigidbody2D rb;
private Animator anim;
public Transform Player;
private bool facingRight = true;
void Start ()
{
rb = GetComponent<Rigidbody2D> ();
anim = GetComponent<Animator> ();
}
void Update()
{
Flip();
}
void Flip()
{
if (Player.transform.localScale.x > 0) {
transform.localScale = new Vector3 (1.69f, 1.54f, 1f);
}
if (Player.transform.localScale.x < 0) {
transform.localScale = new Vector3 (-1.69f, 1.54f, 1f);
}
}
void Flipper()
{
facingRight = !facingRight;
Vector2 theScale = transform.localScale;
theScale.x *= -1;
transform.localScale = theScale;
}
}
Got any ideas? I'd rather avoid using FindGameObject because it's not actually looking for the player script. It's looking for a child transform with no script attached to the player. And because I have two different player GameObjects that you can switch to anytime in the game, it wouldn't really work for me in that regard.
You will need to perform a check of some sort against the players position with the bird position if you want it to face the player at all times. A barebones method would just be to compare the x-positions of the two objects and change the scale accordingly.
void Update()
{
transform.localScale = new Vector3(getDir()*1.69f, 1.54f, 1);
}
private int getDir()
{
if (player.transform.position.x < transform.position.x)
return -1;
else
return 1;
}
You should throw some additional checks in here to keep it from updating the scale every frame when there is no change.

How to change lane smoothly

I am trying to achieve basic racing game. Infinite racing game, movement method like a subway surfers. I have a problem about changing lane. I dont want to teleport to other lane, I want to smoothly. I am newbee in unity, I have try Lerp method but it is not working.
using UnityEngine;
using System.Collections;
public class VehicleController : MonoBehaviour
{
public float drift;
public Vector3 positionA;
public Vector3 positionB;
public Vector3 positionC;
public Vector3 positionD;
private Transform tf;
private Rigidbody rb;
private Vector3 vehiclePos;
void Awake()
{
//rb = GetComponent<Rigidbody> ();
tf = transform;
}
void Update()
{
vehiclePos = tf.position;
if (Input.GetKey( KeyCode.Space ))
DecreaseSpeed ();
else
IncreaseSpeed ();
if (Input.GetKeyDown (KeyCode.A))
{
MoveToRight ();
Debug.Log( "Move to Right!" );
}
if (Input.GetKeyDown (KeyCode.D))
{
MoveToLeft ();
Debug.Log( "Move to Left!" );
}
}
void FixedUpdate()
{
tf.Translate (Vector3.forward * speed * Time.deltaTime);//My Movement Method.
}
void MoveToLeft()
{
if (vehiclePos.position.x == positionA.x)
vehiclePos = Vector3.Lerp (vehiclePos.position, positionB, Time.deltaTime * drift);
}
void MoveToRight()
{
if (vehiclePos.position.x == positionB.x)
vehiclePos = Vector3.Lerp (vehiclePos.position, positionA, Time.deltaTime * drift);
}
}
First: Don't use == for position.x, since it's a floating-point (decimals) value and in this case it would be very rare for it to return "true". Here's some info about comparing floats.
Second: It doesn't look like you're connecting your actual position with vehiclePos anywhere. transform.position is what you want there.
Third: Input.GetAxis() is a cleaner way to deal with direction input. Instead of specifically calling out each button you can deal with just one float value between -1 and 1. It will also let you reconfigure the keys easily.
Fourth: In an infinite runner it is better to have the world move towards your character and camera than to have the character and camera actually move forward. Floating point numbers get less precise as you move further away from zero, so you should have your action take place relatively close to the world origin (0,0,0) point if you can.
If you want to press the button once to change lanes, you should keep an integer variable that saves which lane you're currently in. If you press LEFT you subtract one, and if you press RIGHT you add one. You should also add a check to make sure it stays within the desired range.
Then in Update() you just need to ALWAYS Lerp towards that X value. You can use Mathf.Lerp to lerp only one variable at a time if you want.
public int laneNumber = 0;
public int lanesCount = 4;
bool didChangeLastFrame = false;
public float laneDistance = 2;
public float firstLaneXPos = 0;
public float deadZone = 0.1f;
public float sideSpeed = 5;
void Update() {
//"Horizontal" is a default input axis set to arrow keys and A/D
//We want to check whether it is less than the deadZone instead of whether it's equal to zero
float input = Input.GetAxis("Horizontal");
if(Mathf.Abs(input) > deadZone) {
if(!didChangeLastFrame) {
didChangeLastFrame = true; //Prevent overshooting lanes
laneNumber += Mathf.roundToInt(Mathf.Sign(input));
if(laneNumber < 0) laneNumber = 0;
else if(laneNumber >= lanesCount) laneNumber = lanesCount - 1;
}
} else {
didChangeLastFrame = false;
//The user hasn't pressed a direction this frame, so allow changing directions next frame.
}
Vector3 pos = transform.position;
pos.x = Mathf.Lerp(pos.x, firstLandXPos + laneDistance * laneNumber, Time.deltaTime * sideSpeed);
transform.position = pos;
}
You could likely just use this code as-is, but I suggest you look it over and try to figure out an understanding of how and why it works. A newbie today who always seeks to improve their skill can do something amazing next week. Hope this helps. :)

Categories