S14: TileVania, L336: Making Enemies: How do I get my left-facing and left-moving Enemy to recognize a wall and turn around?
Unlike Rick, I wanted my first enemy to be facing Left and moving Left, so I created a left-facing sprite and animation. The problem is, it doesn’t work with the script the way his is. The only solution I found that works for an enemy walking on a platform left, then right at the edge, BUT the problem is for my enemy below who walks Left, collides with a wall (rather than a ledge) and gets stuck in an infinite one-step Left-Right, Right-Left motion.
The bottom enemy (one the left) is stuck in a perpetual Left-Right, Left-Right.
Here’s my Player.cs code, (The enemy script is another file is exactly as Rick has it, this player code being modified):
[You can ignore the added jumping animation part which works fine]
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityStandardAssets.CrossPlatformInput;
public class Player : MonoBehaviour
{
// config parameters
[SerializeField] float runSpeed = 8f;
[SerializeField] float jumpHeight = 5f;
[SerializeField] float climbSpeed = 5f;
[SerializeField] Vector2 deathKick = new Vector2(25f, 25f);
// State variables
bool isAlive = true;
//cached component references
Rigidbody2D myRigidBody2D;
Animator myAnimator;
CapsuleCollider2D myCollider2D;
float gravityScaleAtStart;
BoxCollider2D playerFeet;
// state variables
// Use this for initialization
void Start()
{
myRigidBody2D = GetComponent<Rigidbody2D>();
myAnimator = GetComponent<Animator>();
myCollider2D = GetComponent<CapsuleCollider2D>();
gravityScaleAtStart = myRigidBody2D.gravityScale;
playerFeet = GetComponent<BoxCollider2D>();
}
// Update is called once per frame
void Update()
{
if(!isAlive) { return; }
// if dead then skip the remaining statements in this update function.
// otherwise you can call these functions.
Run();
LookLeftOrRight();
Jump();
ClimbLadder();
Die();
}
private void LookLeftOrRight()
{ // if the player is moving horizontally...
bool playerHasHorizontalSpeed = Mathf.Abs(myRigidBody2D.velocity.x) > Mathf.Epsilon;
// mathf.epsilon gives an almost zero value.
if (playerHasHorizontalSpeed)
{
transform.localScale = new Vector2(Mathf.Sign(myRigidBody2D.velocity.x), 1f);
//localScale is simply the scale parameter in inspector under transform.
// reverse the current scaling for x axis
// use mathf.abs to get a positive number.
// use mathf.sign to give a negative sign or positive.
}
}
private void Jump()
{
if (!playerFeet.IsTouchingLayers(LayerMask.GetMask("Ground")))
{ return; }
if (CrossPlatformInputManager.GetButtonDown("Jump"))
{ // give ourselves a new y velocity:
Vector2 jumpVelocityToAdd = new Vector2(0f, jumpHeight);
myRigidBody2D.velocity += jumpVelocityToAdd;
}
// My code that gets jump animation working and stopping when it should:
bool playerIsJumping = Mathf.Abs(myRigidBody2D.velocity.y) > Mathf.Epsilon;
if (playerIsJumping)
{
myAnimator.SetBool("Jumping", true);
} else if (!playerIsJumping)
{
myAnimator.SetBool("Jumping", false);
}
}
private void Run()
{
float controlHorizontal = CrossPlatformInputManager.GetAxis("Horizontal");
// value is between -1 (left) and 1 (right)
Vector2 playerVelocity = new Vector2(controlHorizontal * runSpeed, myRigidBody2D.velocity.y);
// controlHoriz * runSpeed makes it from -8 to +8
// seems it HAS to be set manually in unity. so i matched it to 8.
myRigidBody2D.velocity = playerVelocity;
bool playerHasHorizontalSpeed = Mathf.Abs(myRigidBody2D.velocity.x) > Mathf.Epsilon;
myAnimator.SetBool("Walking", playerHasHorizontalSpeed);
// instead of this if, you could also replace the bool variable name where it says true
// which equates to the same thing. Done this one.
// original code: if (playerHasHorizontalSpeed) { myAnimator.SetBool("Walking", true); }
// the new line that includes playerHas...in the SetBool has the added benefit
// of stopping the player from having the run animation when horizontal speed stops.
}
private void ClimbLadder()
{
if (!playerFeet.IsTouchingLayers(LayerMask.GetMask("Climbing")))
{
myAnimator.SetBool("Climbing", false);
// this above line ensures climbing animation is off when were not touching the ladder.
myRigidBody2D.gravityScale = gravityScaleAtStart;
// anytime you're not on the ladder set gravity scale to what it is normally.
return;
}
myAnimator.SetBool("Jumping", false);
// this above line sets jumping animation to false when we ARE touching the ladder.
float controlClimbing = CrossPlatformInputManager.GetAxis("Vertical");
// get up/down input and assign to controlClimbing variables.
Vector2 climbVelocity = new Vector2(myRigidBody2D.velocity.x, controlClimbing * climbSpeed);
// set climb veolcity to what it already is on x-axis, and on the y-axis, get vertical input
// and multiply by climbSpeed which we've set to 5 for now but is serialized (editable in unity).
myRigidBody2D.velocity = climbVelocity;
myRigidBody2D.gravityScale = 0f;
// set gravity of rigidbody to zero whilst on the ladder.
bool playerIsClimbing = Mathf.Abs(myRigidBody2D.velocity.y) > Mathf.Epsilon;
myAnimator.SetBool("Climbing", playerIsClimbing);
}
private void Die()
{
if(myCollider2D.IsTouchingLayers(LayerMask.GetMask("Enemy", "Hazards")))
{
isAlive = false;
myAnimator.SetTrigger("Dying");
GetComponent<Rigidbody2D>().velocity = deathKick;
// playerFeet.enabled = false;
}
}
}
Here’s the project as it currently stands (using Unity 2018.2.14f1)
Any help would be much appreciated.