I'm trying to get a sprite to move along a set of waypoints after spawned, but whenever the sprite gets to the first waypoint it just stops.
Inspector for the road object that contains all the waypoints
public class MoveEnemy : MonoBehaviour
{
[HideInInspector]
public GameObject[] waypoints;
private int currentWaypoint = 0;
private float lastWaypointSwitchTime;
public float speed = 1.0f;
// Start is called before the first frame update
void Start()
{
lastWaypointSwitchTime = Time.time;
}
// Update is called once per frame
void Update()
{
// 1
Vector3 startPosition = waypoints[currentWaypoint].transform.position;
Vector3 endPosition = waypoints[currentWaypoint + 1].transform.position;
// 2
float pathLength = Vector3.Distance(startPosition, endPosition);
float totalTimeForPath = pathLength / speed;
float currentTimeOnPath = Time.time - lastWaypointSwitchTime;
gameObject.transform.position = Vector2.Lerp(startPosition, endPosition, currentTimeOnPath / totalTimeForPath);
// 3
if (gameObject.transform.position.Equals(endPosition))
{
if (currentWaypoint < waypoints.Length - 2)
{
// 3.a
currentWaypoint++;
lastWaypointSwitchTime = Time.time;
// TODO: Rotate into move direction
}
else
{
// 3.b
Destroy(gameObject);
AudioSource audioSource = gameObject.GetComponent<AudioSource>();
AudioSource.PlayClipAtPoint(audioSource.clip, transform.position);
// TODO: deduct health
}
}
}
}
To solve your problem try replacing
if (gameObject.transform.position.Equals(endPosition))
with
if (Vector2.Distance(gameObject.transform.position, endPosition) < 0.2f)
This should fix the issue where the gameObject stops at the first waypoint. This is because the position of the gameObject and the waypoint may not be the same down to the decimal, so this code will check if it's in a range of 0.2 from the next waypoint.
Also calling these two lines:
Vector3 startPosition = waypoints[currentWaypoint].transform.position;
Vector3 endPosition = waypoints[currentWaypoint + 1].transform.position;
In the update function isn't very efficient. You could instead calculate these values in the start function, and also when you reach the next waypoint.
Hope this helps!
Related
so I'm making a 2D platformer and I would like to spawn enemies on the whole map but not in the camera range.
I have to mention I am using Cinemachine to follow the player.
This is my script for the spawner:
*public class MobSpawner : MonoBehaviour
{
public GameObject Mob;
float randX;
Vector2 whereToSpawn;
public float spawnRate = 2f;
float nextSpawn = 0.0f;
public GameObject Camera;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if (Time.time > nextSpawn)
{
nextSpawn = Time.time + spawnRate;
randX = Random.Range(-36f, 62);
whereToSpawn = new Vector2(randX, transform.position.y);
Instantiate(Mob, whereToSpawn, Quaternion.identity);
}
}
}*
As you can see I'm taking 2 points and in between them I spawn enemies. As this range would basically be the entire map, the camera will always be in it.
I want enemies to NOT spawn inside the camera yet spawn outside of it. How can I manage to do this?
I'd say the simplest solution would be to just calculate vector distance between the player position and spawning position. Since this is a 2d space and you are spawning them only on one axis it should be fine.
So you could do
float distanceAwayFromCameraView = 5;
// you will need a method, reference or just get the object via tag
var playerPos = GameObject.Find("Player").transform.position;
randX = Random.Range(-36f, 62);
whereToSpawn = new Vector2(randX, transform.position.y);
while ((Vector2.Distance(whereToSpawn, playerPos) < distanceAwayFromCameraView){
randX = Random.Range(-36f, 62);
whereToSpawn = new Vector2(randX, transform.position.y);
}
Instantiate...
I'm not at my pc to test this right now, please let me know if there's errors
Hello everyone and thanks in advance for your answers!
I am a beginner in unity, coding my second game after i finshed a couple of tutorials.
Since today, i noticed that suddenly all my "GameObjects" have a "UnityEngine." in front of them.
I have no idea how that happened, and it is also not just in one script, but all of them. Heres an example of it:
UnityEngine.GameObject item = (UnityEngine.GameObject)Instantiate(itemGOList[i], spawnTransform, spawnRotation);
This worked fine before without the "UnityEngine.", but now it only works when its written this way.
Do you know how this could have happened and how to revert it?
This is one of the scripts:
using UnityEngine;
using UnityEngine.EventSystems;
public class Turret : MonoBehaviour
{
private Transform target;
private Enemy targetEnemy;
[Header("General")]
public float range = 10f;
[Header("Use Bullets/missiles (default)")]
public UnityEngine.GameObject bulletPrefab;
public float fireRate = 1f;
private float fireCountdown = 0f;
public AudioClip shootingSound;
private AudioSource audioSource;
[Header("Use Laser (default)")]
public bool useLaser = false;
public int damageOverTime = 20;
public float slowAmount = .5f;
public LineRenderer lineRenderer;
public ParticleSystem impactEffect;
public Light impactLight;
[Header("Unity Setup Fields")]
public string enemyTag = "Enemy";
public Transform partToRotate;
public float turnSpeed = 10f;
public Transform firePoint;
void Start()
{
InvokeRepeating(nameof(UpdateTarget), 0f, 0.3f); // Call the UpdateTarget Method after 0 seconds, then repeat every 0.3 seconds.
audioSource = GetComponent<AudioSource>(); //Puts the AudioSource of this GO (the turret) into the variable audioSource.
}
void UpdateTarget ()
{
UnityEngine.GameObject[] enemies = UnityEngine.GameObject.FindGameObjectsWithTag(enemyTag); // Find all enemies (and store in a list?).
float shortestDistance = Mathf.Infinity; // Create a float for the shortest distance to an enemy.
UnityEngine.GameObject nearestEnemy = null; // Create a variable which stores the nearest enemy as a gameobject.
foreach (UnityEngine.GameObject enemy in enemies) // Loop through the enemies array.
{
float distanceToEnemy = Vector3.Distance(transform.position, enemy.transform.position); //Get the distance to each enemy and stores it.
if (distanceToEnemy < shortestDistance) // If any of the enemies is closer than the original, make this distance the new shortestDistance and the new enemy to the nearestEnemy.
{
shortestDistance = distanceToEnemy;
nearestEnemy = enemy;
}
}
if (nearestEnemy != null && shortestDistance <= range) // Sets the target to the nearestEnemy (only if its in range and not null).
{
target = nearestEnemy.transform;
targetEnemy = nearestEnemy.GetComponent<Enemy>();
}
else
{
target = null;
}
}
void Update()
{
if (target == null) // If there is no target, do nothing.
{
if (useLaser)
{
if (lineRenderer.enabled)
{
lineRenderer.enabled = false;
impactEffect.Stop();
impactLight.enabled = false;
audioSource.Stop();
}
}
return;
}
LockOnTarget();
if (useLaser)
{
Laser();
}
else
{
if (fireCountdown <= 0f)
{
Shoot();
fireCountdown = 1f / fireRate;
}
fireCountdown -= Time.deltaTime;
}
}
void Laser()
{
targetEnemy.TakeDamage(damageOverTime * Time.deltaTime);
targetEnemy.Slow(slowAmount);
if (!lineRenderer.enabled)
{
lineRenderer.enabled = true;
impactEffect.Play();
impactLight.enabled = true;
if (audioSource.isPlaying == false)
{
audioSource.Play();
}
}
lineRenderer.SetPosition(0, firePoint.position);
lineRenderer.SetPosition(1, target.position);
Vector3 dir = firePoint.position - target.position;
impactEffect.transform.position = target.position + dir.normalized * 1f;
impactEffect.transform.rotation = Quaternion.LookRotation(dir);
}
void LockOnTarget()
{
Vector3 dir = target.position - transform.position; // Store the direction from turret to target.
Quaternion lookRotation = Quaternion.LookRotation(dir); // I have no idea how this works...
Vector3 rotation = Quaternion.Lerp(partToRotate.rotation, lookRotation, Time.deltaTime * turnSpeed).eulerAngles; // Convert quaternion angles to euler angles and Lerp it (smoothing out the transition).
partToRotate.rotation = Quaternion.Euler(0f, rotation.y, 0f); // Only rotate around the y-axis.
}
void Shoot()
{
UnityEngine.GameObject bulletGO = (UnityEngine.GameObject)Instantiate(bulletPrefab, firePoint.position, firePoint.rotation); // Spawn a bullet at the location and rotation of firePoint. (Also makes this a GO to reference it.
Bullet bullet = bulletGO.GetComponent<Bullet>(); // I have no idea how this works...
audioSource.PlayOneShot(shootingSound, 0.2f);
if (bullet != null) // If there is a bullet, use the seek method from the bullet script.
{
bullet.Seek(target);
}
}
void OnDrawGizmosSelected() // Do this method if gizmo is selected.
{
Gizmos.color = Color.red;
Gizmos.DrawWireSphere(transform.position, range); // Draw a wire sphere on the selected point (selected turret f. e.) and give it the towers range.
}
}
The problem was that i had a script called "GameObject" in my Unity Project.
I was also able to rename all "UnityEngine.GameObject" to "Gameobject" by using the Quick Action in Visual Studio.
Hey Guys I've been having a problem lately that I cant seem to solve.
A sprite is supposed to roam around (as it does) while nothing is inside its radius, however if the player moves close to it the sprite should theoretically move towards it and stop roaming.
The sprite doesn't follow the player and cant even see its tag since I cant even see the contents of the "Collider2D[] hits".
using System.Collections.Generic;
using UnityEngine;
public class FireCultist : MonoBehaviour
{
public float moveTimeSeconds; //Time it will take object to move, in seconds.
private float xMax = 10.0f; // The boundaries of the spawn area
private float yMax = 10.0f;
private float xMin = -10.0f; // The boundaries of the spawn area
private float yMin = -10.0f;
public int xDistanceRange; // The max distance you can move at one time
public int yDistanceRange;
private BoxCollider2D boxCollider; //The BoxCollider2D component attached to this object.
private Rigidbody2D rb2D; //The Rigidbody2D component attached to this object.
private float inverseMoveTime; //Used to make movement more efficient.
public Vector2 start;
public Vector2 end;
public bool roam = true;
public Collider2D[] hits;
void Start()
{
boxCollider = GetComponent<BoxCollider2D>();
rb2D = GetComponent<Rigidbody2D>();
inverseMoveTime = 1f / moveTimeSeconds;
InvokeRepeating("RandomMove", 0.0f, 5.0f);
}
void Update()
{
Collider2D[] hits = Physics2D.OverlapCircleAll(transform.position, 10); // returns all colliders within radius of enemy
int i = 0;
while(hits.Length > i)
{
Debug.Log("Sees");
Debug.Log(hits[i]);
i++;
}
followPlayer();
if (roam)
{
Debug.Log("Roam");
transform.position = Vector2.MoveTowards(transform.position, end, inverseMoveTime * Time.deltaTime); //Random move to new position
}
}
public void followPlayer()
{
foreach (Collider2D hit in hits)
{
if (hit.tag == "Player") // if the player is within a radius
{
Debug.Log("Chasing Player");
transform.position = Vector2.MoveTowards(transform.position, GameObject.Find("Prefabs/Player").GetComponent<Transform>().position, inverseMoveTime * Time.deltaTime); // chase player
roam = false;
}
else
{
Debug.Log("Continues");
roam = true; // Continue RandomMove()
}
}
}
public void RandomMove() // gets random coordinates near enemy and moves there
{
float xDistance = Random.Range(-xDistanceRange, xDistanceRange); // check
float yDistance = Random.Range(-yDistanceRange, yDistanceRange);// check
if (xDistance < xMax && xDistance > xMin && yDistance < yMax && yDistance > yMin && roam == true) // If within boundaries of walking space
{
start = transform.position;
end = start + new Vector2(xDistance, yDistance);
Debug.Log("Roaming From : " + start + " To : " + end);
}
}
}
The roaming algorithm works however not too sure about the tag detection.
The script belongs to this enemy object
Player Object Properties
It looks like you are declaring a new hits variable during every update instead of using your class-level variable. This means the variable inside of followPlayer() will never be instantiated, and the information will not be passed between the two methods.
Try this:
void Update()
{
hits = Physics2D.OverlapCircleAll(transform.position, 10);
//...
}
You might also consider attaching a trigger collider to the enemy which sends a notification when the player enters/exits the trigger instead of relying on OverlapCircleAll()
void OnTriggerEnter2D(Collider2D col)
{
if(col.gameObject.tag == "Player"){
roam = false;
}
}
void OnTriggerExit2D(Collider2D col)
{
if(col.gameObject.tag == "Player"){
roam = true;
}
}
I'm trying to write a script for my enemy movements. Here is my code:
public class EnemiesMovement : MonoBehaviour
{
private bool isFacingRight = false;
public float speed = 10f;
private float startPoint;
private float endPoint;
public float unitMovement = 2f;
private Rigidbody2D enemy;
private Animator anim;
bool moveRight;
// Start is called before the first frame update
void Start()
{
enemy = GetComponent<Rigidbody2D>();
anim = GetComponent<Animator>();
startPoint = enemy.position.x;
endPoint = startPoint + unitMovement;
}
// Update is called once per frame
void Update()
{
if (enemy.position.x >= endPoint)
moveRight = false;
if (enemy.position.x <= startPoint)
moveRight = true;
if (moveRight)
{
enemy.velocity = new Vector2(-transform.localScale.x, 0) * speed;
if (!isFacingRight)
Flip();
}
if (!moveRight)
{
enemy.velocity = new Vector2(transform.localScale.x, 0) * speed;
if (isFacingRight)
Flip();
}
}
void Flip()
{
isFacingRight = !isFacingRight;
Vector3 scale = transform.localScale;
scale.x *= -1;
transform.localScale = scale;
}
}
The movement of the enemy is right, but after the enemy flips its sprites, it doesn't change the direction of its movement. Basically, it continues to the right, even though its sprite has turned left. Can someone show me how I can fix this?
My enemy spite has a positive scale facing to the left.
Velocity is affected by the scale property.
So if you change the scale negative, everything on this GameObject flips, it's not necessary to change the velocity direction.
Are you getting bewildered? You can try another method to flip the sprite.
GetComponent<SpriteRenderer>().flipX = true;
This only flips the sprite renderer, other things are working as normal in its world space.
Another suggests as Unity documented it's better to change Rigidbody's property in the FixedUpdate.
I mean that when the character is walking or running and getting to the clicked point he change the state in the animator from walk/run to idle so it looks like he walk then stop there is no animation between the walk/run and the start/stop.
I have 3 states in the animator. HumanoidWalk, HumanoidRun, HumanoidIdle.
I need something like fading.
For example if in the line:
_animator.CrossFade("Walk", 0);
I will change the 0 to 1 so when he start "Walk" it will be a bit slowly to walking speed. But in "Idle" if i will change it to 1 it will be something else not fading until he stop.
In other words i want to add fading effects when character start/stop walking/running and also when i click/double click and he switch between Walk and Run. Make some fade effects so it will not switch between the states so fast.
using UnityEngine;
using System.Collections;
public class ClickToMove : MonoBehaviour
{
public int speed = 5; // Determines how quickly object moves towards position
public float rotationSpeed = 5f;
private Vector3 targetPosition;
private Animator _animator;
private Vector3 destination;
private Quaternion targetRotation;
public float clickDelta = 0.35f; // Max between two click to be considered a double click
private bool click = false;
private float clickTime;
void Start()
{
_animator = GetComponent<Animator>();
_animator.CrossFade("Idle", 0);
}
void Update()
{
if (Input.GetKeyDown(KeyCode.Mouse0))
{
if (click && Time.time <= (clickTime + clickDelta))
{
_animator.CrossFade("Run", 0);
click = false;
}
else
{
click = true;
clickTime = Time.time;
}
_animator.CrossFade("Walk", 0);
Plane playerPlane = new Plane(Vector3.up, transform.position);
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
float hitdist = 0.0f;
if (playerPlane.Raycast(ray, out hitdist))
{
Vector3 targetPoint = ray.GetPoint(hitdist);
targetPosition = ray.GetPoint(hitdist);
targetRotation = Quaternion.LookRotation(targetPoint - transform.position);
destination = targetPosition;
}
}
transform.rotation = Quaternion.RotateTowards(transform.rotation, targetRotation, rotationSpeed);
if ((transform.position - destination).magnitude < 0.7f)
{
_animator.CrossFade("Idle", 0);
}
}
}
If you increase your transform duration value a little (like 0.25), then you can get a smooth transition between states. Also uncheck "fixed duration".