# Enemy AI on a platform

I was working on a 2.5D enemy AI movement on a platform. The idea is to get the enemy(green cubes) to move along the platform, when it gets to the edge of the platform, it turns around and goes the opposite direction, if it meets another enemy, it should turn around and go the opposite direction.

My code works and the enemy moves as I expected on the editor, but when I build it out to mobile, the enemy develops a weird movement(like it is trying to go in both directions at the same time).

Here's a playtest on the editor: https://youtu.be/fmZSHd3GXDE

Here's a playtest on mobile: https://youtu.be/xBDDdgVrGQE

I can't find out what could be the cause. What do you think?

Here is my code:

private Rigidbody mRigidbody = null;

private bool isIdle = true;
private bool turnedLeft = false;
private bool turnedRight = false;

private Transform currentObstacle = null;
private Transform currentPlatformEdge = null;
private Transform leftMostPlatformEdge = null; //transform that represents left end of a platform
private Transform rightMostPlatformEdge = null; //transform that represents the right end of a platform

private int randomNumber = 0;

private float lookDirection = 1f;

[SerializeField] private float walkSpeed = 92f;
[SerializeField] private float gravityMultiplier = 2f;
[SerializeField] private float rayDistanceToObstacle = 1.25f;

private Vector2 right = Vector2.zero;

private void Awake()
{
mRigidbody = GetComponent<Rigidbody>();
}

void Start()
{
randomNumber = Random.Range(0, 2);
currentPlatformEdge = leftMostPlatformEdge;
}

private void FixedUpdate()
{
ApplyGravity();

right = transform.TransformDirection(Vector2.right);

CastRay();

}

private void ApplyGravity()
{
Vector2 extraGravityForce = (Physics.gravity * gravityMultiplier) - Physics.gravity;
}

private void CastRay()
{
Ray ray = new Ray(transform.position, Vector2.right * lookDirection);

Vector2 startPos = transform.position;
startPos.y += 1f;

#region Obstacle Ray
bool HittingObstacle = Physics.Raycast(ray, out RaycastHit obstacleHit, rayDistanceToObstacle);
if (HittingObstacle)
{
if (obstacleHit.collider.CompareTag("Obstacle") || obstacleHit.collider.CompareTag("Enemy")
|| obstacleHit.collider.CompareTag("Platform"))
currentObstacle = obstacleHit.transform;
}
else if (!HittingObstacle && currentPlatformEdge != null)
currentObstacle = null;
#endregion
}

{
Vector2 CurrentTargetDistanceFromTransform = Vector2.zero, CurrentPlatformEdgeDistanceFromTransform = Vector2.zero;
if (currentObstacle != null)
CurrentTargetDistanceFromTransform = currentObstacle.position - transform.position;
if (currentPlatformEdge != null)
CurrentPlatformEdgeDistanceFromTransform = currentPlatformEdge.position - transform.position;

if (currentObstacle != null || currentPlatformEdge != null)
{
if (currentObstacle != null) //If an enemy or an object with the obstacle tag is in this transform's way
{
if (Vector2.Dot(right, CurrentTargetDistanceFromTransform) < 0)
WalkLeft();
else
WalkRight();
}
else if (currentPlatformEdge != null && currentObstacle == null)
{
if (Vector2.Dot(right, CurrentPlatformEdgeDistanceFromTransform) < 0)
WalkRight();
else
WalkLeft();
}
}
else
WalkTowardsAnyDirection();
}

private void TurnLeft()
{
Quaternion Rot = Quaternion.Euler(0, -90, 0);
transform.rotation = Rot;

lookDirection = -1;

mRigidbody.velocity = new Vector2(0, mRigidbody.velocity.y);

turnedLeft = true;
turnedRight = false;
}

private void TurnRight()
{
Quaternion Rot = Quaternion.Euler(0, 90, 0);
transform.rotation = Rot;

lookDirection = 1;

mRigidbody.velocity = new Vector2(0, mRigidbody.velocity.y);

turnedRight = true;
turnedLeft = false;
}

private void WalkLeft()
{
isIdle = false;

if (!isIdle)
{
if (!turnedLeft)
TurnLeft();

mRigidbody.velocity = new Vector2(-walkSpeed * Time.fixedDeltaTime, mRigidbody.velocity.y);
}

if (leftMostPlatformEdge != null)
currentPlatformEdge = leftMostPlatformEdge;
else
currentPlatformEdge = null;
}

private void WalkRight()
{
isIdle = false;

if (!isIdle)
{
if (!turnedRight)
TurnRight();

mRigidbody.velocity = new Vector2(walkSpeed * Time.fixedDeltaTime, mRigidbody.velocity.y);
}

if (rightMostPlatformEdge != null)
currentPlatformEdge = rightMostPlatformEdge;
else
currentPlatformEdge = null;
}

protected void WalkTowardsAnyDirection()
{
if (randomNumber == 0)
WalkLeft();
else
WalkRight();
}

private void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.CompareTag("Platform"))
{
leftMostPlatformEdge = collision.gameObject.transform.GetChild(0).transform;
rightMostPlatformEdge = collision.gameObject.transform.GetChild(1).transform;

WalkTowardsAnyDirection();
return;
}
}

private void OnCollisionExit(Collision collision)
{
if (collision.gameObject.CompareTag("Platform"))
{
leftMostPlatformEdge = null;
rightMostPlatformEdge = null;
}
}

• Your sourcecode appears to contain lots and lots of code which does not seem related to the problem at all. That makes it much harder to analyze. Can you try to reduce your example to the smallest possible amount which still reproduces the behavior you are experiencing? Sep 14, 2020 at 12:46
• Could it be possible that the objects are detecting themselves and thus change direction on every update? Sep 14, 2020 at 12:48
• This is the only script on this project, I have reduced it to the lowest possible to show the enemy movement. Sep 14, 2020 at 12:54

I have no idea why it works, but when I use:

    if (currentObstacle != null) //If an enemy or an object with the obstacle tag is in this transform's way
{
if (Vector2.Dot(right, CurrentTargetDistanceFromTransform) < 0)
WalkRight();
else
WalkLeft();
}
else if (currentPlatformEdge != null && currentObstacle == null)
{
if (Vector2.Dot(right, CurrentPlatformEdgeDistanceFromTransform) < 0)
WalkLeft();
else
WalkRight();
}


everything works fine on mobile, but the strange movements will be seen on the editor.

My final solution was to use:

        if (currentObstacle != null)
{
Vector2 CurrentTargetDistanceFromTransform = currentObstacle.position - transform.position;

if (Vector2.Dot(right, CurrentTargetDistanceFromTransform) < 0)
{


#if UNITY_ANDROID

                WalkRight();


#endif #if UNITY_EDITOR

                WalkLeft();


#endif

            }
else
{


#if UNITY_ANDROID

                WalkLeft();


#endif #if UNITY_EDITOR

                WalkRight();


#endif

            }
}
else if (currentPlatformEdge != null && currentObstacle == null)
{
Vector2 CurrentPlatformEdgeDistanceFromTransform = currentPlatformEdge.position - transform.position;

if (Vector2.Dot(right, CurrentPlatformEdgeDistanceFromTransform) < 0)
{


#if UNITY_ANDROID

                WalkLeft();


#endif #if UNITY_EDITOR

                WalkRight();


#endif

            }
else
{


#if UNITY_ANDROID

                WalkRight();


#endif #if UNITY_EDITOR

                WalkLeft();


#endif

            }
}


If anyone knows why it works this way, I'll love to hear it