Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more..
    Dismiss Notice
  3. Dismiss Notice

Make a enemy strafe (zigzag) in one direction

Discussion in 'Scripting' started by T0lken, Mar 6, 2020.

  1. T0lken

    T0lken

    Joined:
    Mar 5, 2020
    Posts:
    5
    Hello, i am new to c# programming. I have tried to write a script for a enemy to strafe(zigzag) down towards the bottom of the screen. ( top down view ) and i cant get it right.
    And yeah i have tried the
    Mathf.PingPong method but i dosent realy do what i need it to do. Since i want it to be more random then that.

    Do you have any suggestions?

    Thanks.

    This is my code:

    private float rangeX = 2f;
    private float speed = 3;


    // Update is called once per frame
    void Update()
    {

    transform.Translate(Vector3.forward * Time.deltaTime * speed);
    transform.Translate(Vector3.left * Time.deltaTime * speed);

    if (transform.position.x < -rangeX)

    {
    transform.Translate(Vector3.left * Time.deltaTime * speed);
    }
    if (transform.position.x > rangeX)

    {
    transform.Translate(Vector3.right * Time.deltaTime * speed);
    }
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,769
    Usually for enemies you would use a timer and some values to cause it to behave differently during those time intervals.

    Lets say he decides "Hey, I'm gonna strafe!" He would pick an amount of time (or distance) to move left/right, and then do that while he is counting down those timers to return to standing still.

    Also, be aware of the difference between
    Vector3.right
    and
    transform.right
    : the former is in world space (does not change based on the player's heading), and the latter is in the coordinate space of the transform itself. If your transform is rotated to
    Quaternion.identity
    , only then are these properties identical. Same goes for
    transform.up
    and
    transform.forward
    .

    Note also there is no
    transform.back
    : you just negate
    transform.forward
    . Same goes for
    transform.left
    and
    transform.down
    .
     
  3. ashkanaral

    ashkanaral

    Joined:
    Sep 3, 2018
    Posts:
    22
    You probably already transforming down then to the left in your first two lines in Update() unless you want to move diagonally? The rest of your code seems right without testing.

    Code (CSharp):
    1. private float rangeX = 2f;
    2. private float speed = 3;
    3.  
    4.  
    5. // Update is called once per frame
    6. void Update()
    7. {
    8.  
    9. transform.Translate(Vector3.forward * Time.deltaTime * speed);
    10. // transform.Translate(Vector3.left * Time.deltaTime * speed); // delete this line if you want to go straight down
    11.  
    12. if (transform.position.x < -rangeX)
    13.  
    14. {
    15. transform.Translate(Vector3.left * Time.deltaTime * speed);
    16. }
    17. if (transform.position.x > rangeX)
    18.  
    19. {
    20. transform.Translate(Vector3.right * Time.deltaTime * speed);
    21. }
     
  4. T0lken

    T0lken

    Joined:
    Mar 5, 2020
    Posts:
    5
    Hey, thank you for you answer. Hmm it where kind of hard for me to explain this. But I have made a GIF to try to explain what I want to achieve!

    On the top red line the enemy's spawn random and "strafe" downward, I want them to stay at the same rotation like illustrated in the GIF. The green box is the enemy. And since they spawn at random X axis I can't calculate the time at one direction. And it takes away the inbuilt ping pong method. And the blue rectangle is the screen, so I have no walls. :D

    Cheers! strafeEnemy.gif
     
  5. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,769
    Your motion GIF is pure lateral bounce. Move in the direction until you reach the lateral limit, then negate the .x component of your motion, lather rinse, repeat. Use a
    Vector3
    or
    Vector2
    for your motion and it becomes as easy as directly adding it to the
    transform.position
    property.
     
  6. ashkanaral

    ashkanaral

    Joined:
    Sep 3, 2018
    Posts:
    22
    Here it is. Vector3.Left goes (-1,0,0) x, y, z from article below. Same with .Forward and .Right.
    So you will have to check the coordinates and see which one fits the screen.

    Code (CSharp):
    1. private float rangeX = .5f;
    2. private float speed = 4;
    3.  
    4.  
    5. // Update is called once per frame
    6. void Update()
    7. {
    8.  
    9. transform.Translate(Vector3.(1,0,0) * Time.deltaTime * speed);
    10.  
    11. if (transform.position.x < -rangeX)
    12.  
    13. {
    14. transform.Translate(Vector3.(-8,0,0) * Time.deltaTime * speed);
    15. }
    16. if (transform.position.x > rangeX)
    17.  
    18. {
    19. transform.Translate(Vector3.(8,0,0) * Time.deltaTime * speed);
    20. }
    https://docs.unity3d.com/ScriptReference/Vector3-left.html

    Hope this works! Good Luck!
     
    Last edited: Mar 7, 2020
  7. T0lken

    T0lken

    Joined:
    Mar 5, 2020
    Posts:
    5
    I really can't get this to work. It does not go the other way when it gets to the limit? :(

    It where really easy when I made the prototype in Godot Engine. I wish I could just use that code or translate it well. The only option I've found is to use RigidBody and add forces, but then the enemy start colliding with each other and gain speed at random places.

    godot code:
    func _process(_delta: float) -> void:
    if get_position().x <= 0+16:
    velocity.x = abs(velocity.x)
    if get_position().x >= get_viewport_rect().size.x-16:
    velocity.x = -abs(velocity.x)
     
  8. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,769
    Okay, here's some direct steps:

    1. make a blank game object that represents the left side
    2. make another one to represent the right side
    3. put a script on your moving object that does the following:

    - has a Vector3 containing its starting velocity
    - has a Vector3 containing its current velocity
    - contains two public fields into which you can drag the left and right limit objects for later use

    In
    Start()
    :
    - set current velocity equal to initial

    Now in
    Update()
    :

    - move the transform.position by the current velocity (make sure to multiply by Time.deltaTime)

    - check if the transform.position.x is less than the transform.position.x of the left bounds
    ---- if it is, then set the .x of your current velocity equal to the
    Mathf.Abs()
    of what it was before

    - check if the transform.position.x is greater than the transform.position.x of the right bounds
    ---- if it is, then set the .x of your current velocity equal to the
    -Mathf.Abs()
    of what it was before

    If you give it a down and to the right (or down and to the left) initial velocity, that will cause it to ping-pong back and forth the way your GIF shows.
     
  9. ashkanaral

    ashkanaral

    Joined:
    Sep 3, 2018
    Posts:
    22
    I believe I fixed the if statement so that the gameobject can move within the the screen. You may have to play with the numbers (8). Then, the y direction was missing because you want it to move top to down if not your direction put it (0, -1, 0), and I placed that there. Hope it works!

    Code (CSharp):
    1. private float rangeX = .5f;
    2. rangeXDiagonal = 9f;
    3. private float speed = 4;
    4.  
    5. // Update is called once per frame
    6. void Update()
    7. {
    8. transform.Translate(Vector3.(0,1,0) * Time.deltaTime * speed);
    9. if (transform.position.x < rangeXDiagonal)
    10. {
    11. transform.Translate(Vector3.(-8,0,0) * Time.deltaTime * speed);
    12. }
    13. if (transform.position.x  > rangeXDiagonal)
    14. {
    15. transform.Translate(Vector3.(8,0,0) * Time.deltaTime * speed);
    16. }
     
  10. T0lken

    T0lken

    Joined:
    Mar 5, 2020
    Posts:
    5
    Thanks for the in-depth description!
    But I must have a brain block, can't get this to work! I must have gotten some of the fundamental skills wrong.
    Do you know any reference code I could study? I don't understand how to set initial velocity without a RigidBody.

    I have manage to do a lot more complex things without any help -.-

    I am sorry, but thanks again for your time trying to explain this to me.
     
  11. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,769
    What I am mentioning above is for you to do your own movement, either without a Rigidbody or with an .isKinematic one, which means Unity Physics will NOT be moving the object, YOU will be moving it in your script.

    If you look at the code pattern by @ashkanaral above, it's very close to what I'm suggesting you try. His line 8 is the part that moves the object by the current velocity times Time.deltaTime.

    Read my post again; it's virtually pseudocode of exactly the steps that must happen.
     
  12. T0lken

    T0lken

    Joined:
    Mar 5, 2020
    Posts:
    5
    That code is not valid it would be something like.

    transform.Translate(0, 0, 1 * Time.deltaTime * speed);

    But the problem reality comes down to how to change the speed. If its in the update method before the if. it will still keep on pushing it in that direction. so lets say I put

    transform.Translate(1, 0, 0, * Time.deltaTime * speed);
    If (transform.position.x > 1)
    transform.Translate(-1, 0, 0 * time deltaTime * speed);

    "this will make the object stand still at 2x)
    as soon as it not > 2x it will start moving towards 2x again.
    So I would need another function I guess then transform.Translate.

    A function that sets the objects transform or such in a direction, and not stop until it told to.
    I am thinking of a bool. like if right is true keep pushing this direction
    edit And WOW I tried that out. it actually worked! :D

    This is my code:

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;

    public class CleverEnemy : MonoBehaviour
    {
    public bool isPlayerMovingRight = true;
    public float strafeSpeed = 1f;
    public float forwardThrust = 0.5f;
    public float boundary = 5f;
    // Start is called before the first frame update
    void Start()
    {

    }

    // Update is called once per frame
    void Update()
    {
    transform.Translate(Vector3.forward * Time.deltaTime * forwardThrust);

    if (transform.position.x > boundary)
    isPlayerMovingRight = false;
    if (transform.position.x < -boundary)
    isPlayerMovingRight = true;

    if (isPlayerMovingRight == true)
    {
    transform.Translate(Vector3.right * Time.deltaTime * strafeSpeed);
    }
    if (isPlayerMovingRight == false)
    {
    transform.Translate(Vector3.left * Time.deltaTime * strafeSpeed);
    }
    }
    }

    works like a charm.

    Thanks everyone that took their time to help me!
     
    Kurt-Dekker likes this.
  13. Photonphill

    Photonphill

    Joined:
    Mar 30, 2021
    Posts:
    18
    Hi,

    I'd just like to say thanks for posting this code. I have been looking for a way to create a zig zag in my enemies and was struggling. After seeing this I managed to slightly modify it for my game. I was so close to this but couldn't quite get it to work.

    You are a star. :)