0
\$\begingroup\$

Still trying to fix my enemy's bugs. The current one is that Im using a step system that moves the enemy towards a certain position like this:

if (attackType == EnemyAttackType.Follow && !obstructed) {
    float step = playerFollowSpeed * Time.deltaTime;
    transform.position = Vector2.MoveTowards(transform.position, col.transform.position, step);
    transform.position += new Vector3(0, noise * noiseStrength, 0);
    break;
}

At the time this made sense, but now that I'm starting to make the enemy more advanced, there are some serious issues. The first being obvious, setting the position means the velocity is always 0 and I have to detect which way the enemy is facing in some slightly more buggy ways. The second is that being as it sets the position without any check for collision, it can clip through objects depending on their scale.

I wanted to set the velocity but the only way i can see this being done is to set the transform.forward to face the point it needs to go to. But since the enemy has a sprite, rotating it would look really strange.

So does anyone know a way I can counteract both of these issues while retaining a smooth movement?

Here is my current full script:

using UnityEngine;

[RequireComponent(typeof(Collider2D))]
[RequireComponent(typeof(Rigidbody2D))]
public class Enemy : MonoBehaviour
{
    [Header("Enemy Settings")]
    public EnemyType enemyType = EnemyType.Walking;
    public float speed = 5f;
    public float maxDistance = 10f;
    public LayerMask groundLayer;

    [Header("Flight Settings")]
    public BoxCollider2D flightBounds;
    public Transform flightBoundsPosition;
    public bool stationaryWhenIdle = false;

    [Header("Attack Settings")]
    public EnemyAttackType attackType = EnemyAttackType.Follow;
    public EnemyAgression enemyAgression = EnemyAgression.Agressive;
    public float playerDetectionRadius = 8f;
    public float playerFollowSpeed = 7f;
    public LayerMask playerLayer;
    public LayerMask viewObstructionLayers;
    public bool useNoise;
    public float noiseStrength;
    public float noiseFrequency;
    public float noise;

    // internals
    private Rigidbody2D rb;
    private bool grounded;
    private Bounds boxBounds;
    bool atPoint = false;
    Vector2 point;
    int dir;
    private bool beenGrounded = false;

    void Start() {
        rb = GetComponent<Rigidbody2D>();
        boxBounds = flightBounds.bounds;
        if (enemyType == EnemyType.Flying) {point = GeneratePoint(true); rb.gravityScale = 0;}

        if (transform.localScale.x > 0) dir = 1;
        else dir = -1;
    }
    
    void Update() {
        if (useNoise) {noise = Mathf.PerlinNoise(noiseFrequency * Time.time, Time.time); noise -= 0.5f;};
        if (enemyType == EnemyType.Walking) {
            CheckGrounded();
            FlipEnemy();
            MoveEnemy(EnemyType.Walking);
            AlignEnemy();
        }
        if (enemyType == EnemyType.Flying) {
            FlipEnemy();
            MoveEnemy(EnemyType.Flying);
            AlignEnemy();
        }
    }

    private void CheckGrounded() {
        Vector2 pos = new Vector2(0, GetComponent<Collider2D>().bounds.min.y);
        grounded = Physics2D.OverlapBox(pos, new Vector2(0.98f, 0.2f), 0, groundLayer);
        if (!beenGrounded && grounded) {beenGrounded = true; GeneratePoint(maxDistance, true);}
    }
    private bool obstructed = false;
    private void MoveEnemy(EnemyType type) {
        if (type == EnemyType.Flying) {
            Collider2D[] collider2Ds = Physics2D.OverlapCircleAll(transform.position, playerDetectionRadius, playerLayer);

            // flight
            if ((!stationaryWhenIdle && collider2Ds.Length == 0) || obstructed) {
                point = GeneratePoint(false);
                float step = speed * Time.deltaTime;
                transform.position = Vector2.MoveTowards(transform.position, point, step);
                transform.position += new Vector3(0, noise * noiseStrength, 0);
            }

            // attack
            if (enemyAgression == EnemyAgression.Passive) return;
            foreach (Collider2D col in collider2Ds) {
                RaycastHit2D[] hits = Physics2D.RaycastAll(transform.position, col.transform.position, Vector2.Distance(transform.position, col.transform.position), viewObstructionLayers);
                if (hits.Length > 0) { obstructed = true; continue; }
                else obstructed = false;
                if (col.transform.position.x > transform.position.x) dir = 1;
                else dir = -1;
                if (attackType == EnemyAttackType.Follow && !obstructed) {
                    float step = playerFollowSpeed * Time.deltaTime;
                    transform.position = Vector2.MoveTowards(transform.position, col.transform.position, step);
                    transform.position += new Vector3(0, noise * noiseStrength, 0);
                    break;
                }
            }
        }
        else {
            // walk
            if (!stationaryWhenIdle) {
                point = GeneratePoint(maxDistance, false);
                rb.velocity = new Vector2(speed * dir, rb.velocity.y);
            }

            // attack
            if (enemyAgression == EnemyAgression.Passive) return;
            Collider2D[] collider2Ds = Physics2D.OverlapCircleAll(transform.position, playerDetectionRadius, playerLayer);
            foreach (Collider2D col in collider2Ds) {
                if (attackType == EnemyAttackType.Follow) {
                    if (col.transform.position.x > transform.position.x) dir = 1;
                    else dir = -1;
                    rb.velocity = new Vector2(speed * dir, rb.velocity.y);
                }
            }
        }
    }
    private void FlipEnemy() {
        if (dir == 1 && transform.localScale.x < 0) {
            Vector2 newScale = transform.localScale;
            newScale.x *= -1;
            transform.localScale = newScale;
        }
        else if (dir == -1 && transform.localScale.x > 0) {
            Vector2 newScale = transform.localScale;
            newScale.x *= -1;
            transform.localScale = newScale;
        }
    }
    private void FlipEnemyDirection() {
        if (dir == 1) {
            Vector2 newScale = transform.localScale;
            newScale.x *= -1;
            transform.localScale = newScale;
            dir = -1;
        }
        else if (dir == -1) {
            Vector2 newScale = transform.localScale;
            newScale.x *= -1;
            transform.localScale = newScale;
            dir = 1;
        }
    }
    private void AlignEnemy() {

    }

    private Vector2 GeneratePoint(bool isStart) {
        if (Vector2.Distance(transform.position, point) < 0.4f) atPoint = true;
        else atPoint = false;
        if (transform.position.x > point.x) dir = -1;
        else dir = 1;
        if (atPoint || isStart) {
            Vector2 position = new Vector2(
                Random.Range(boxBounds.min.x, boxBounds.max.x),
                Random.Range(boxBounds.min.y, boxBounds.max.y)
            );
            if (position.x > transform.position.x) dir = 1;
            else dir = -1;
            return position;
        }
        else return point;
    }
    private Vector2 GeneratePoint(float distance, bool isStart) {
        if (Vector2.Distance(transform.position, point) < 2f) atPoint = true;
        else atPoint = false;
        if (atPoint || isStart) {
            if (!isStart) FlipEnemyDirection();
            Vector2 position = new Vector2(transform.position.x + (distance * dir), transform.position.y);
            return position;
        }
        else return point;
    }

    void OnDrawGizmos() {
        Gizmos.color = Color.yellow;
        Gizmos.DrawWireCube(flightBoundsPosition.position, flightBoundsPosition.localScale);
        Gizmos.color = Color.green;
        Gizmos.DrawWireSphere(point, 2f);
    }
}

public enum EnemyType {
    Stationary,
    Walking,
    Flying
}

public enum EnemyAgression {
    Agressive,
    Passive,
}

public enum EnemyAttackType {
    Follow
}
\$\endgroup\$
4
  • \$\begingroup\$ Have you tried using Rigidbody2D.MovePosition()? \$\endgroup\$
    – Kevin
    Jan 19 at 2:16
  • \$\begingroup\$ That maybe could work, but it still takes a velocity, what i need is a way to convert a direction into a velocity without effecting the objects rotation \$\endgroup\$
    – Pow
    Jan 19 at 9:54
  • \$\begingroup\$ If you set the position using Rigidbody2D.MovePosition() every frame, you can read the velocity property to find out what direction the body moved over the last frame. Also, you should never move 2D Rigidbodies using transform.position. \$\endgroup\$
    – Kevin
    Jan 22 at 20:28
  • \$\begingroup\$ Yes i know that, thats why i was asking. Thanks for the help \$\endgroup\$
    – Pow
    Jan 23 at 15:53

0

You must log in to answer this question.

Browse other questions tagged .