- Home /
Top down 2d character controller sometimes walking backwards
Hey guys, I'm trying to create a character controller that is a clone of Zelda: A Link to the Past and I almost have it nailed down except for one fairly glaring issue, when facing in a direction (down for example) and immediately pressing two keys in an opposite diagonal direction (up and right at the same time for example) my character will walk backwards, can anyone see what I'm doing wrong?
using UnityEngine;
using System.Collections;
public class playerController : MonoBehaviour
{
// Normal Movements Variables
public float playerSpeed = 4.5f;
// Animation and direction variables
private Animator animator;
private enum Direction { Down, Left, Up, Right };
Direction currentDirection;
private int facing;
private bool isMoving = false;
//Store which directions are being used and more importantly, in what order. Also store how many buttons are pressed
private ArrayList directionsList = new ArrayList();
private int numBtnsPressed;
// Use this for initialization
void Start()
{
//get the Animator cooking (you'll need to set this up on your own, I used: http://michaelcummings.net/mathoms/creating-2d-animated-sprites-using-unity-4.3)
animator = this.GetComponent<Animator>();
}
void FixedUpdate()
{
// Move character, GetAxis*Raw* is most of the magic that makes this possible
rigidbody2D.velocity = new Vector2(Input.GetAxisRaw("Horizontal")* playerSpeed * 0.8f,
Input.GetAxisRaw("Vertical") * playerSpeed * 0.8f);
}
// Update is called once per frame
void Update()
{
//Start every update with isMoving false and no buttons pressed, innocent until proven guilty!
isMoving = false;
numBtnsPressed = 0;
//Store the inputs
var vertical = Input.GetAxisRaw("Vertical");
var horizontal = Input.GetAxisRaw("Horizontal");
//If any direction was pressed we are now moving
if (vertical < 0 || vertical > 0 || horizontal < 0 || horizontal > 0) {
isMoving = true;
//Depending on direction set our initial facing
if (vertical < 0) {
currentDirection = Direction.Down;
numBtnsPressed = numBtnsPressed + 1;
} else if (vertical > 0) {
currentDirection = Direction.Up;
numBtnsPressed = numBtnsPressed + 1;
}
if (horizontal < 0) {
currentDirection = Direction.Left;
numBtnsPressed = numBtnsPressed + 1;
} else if (horizontal > 0) {
currentDirection = Direction.Right;
numBtnsPressed = numBtnsPressed + 1;
}
//If only one button is pressed clear our ArrayList to avoid facing and walking in the wrong direction
if (numBtnsPressed <= 1) {
directionsList.Clear();
}
//If the facing is already in the ArrayList don't add it again, we only need it so we know which was pressed first for the right facing
if(!directionsList.Contains(currentDirection)) {
directionsList.Add((int)currentDirection);
}
//Depending on first direction being pressed that is the direction we should animate (as is the zelda way) see website I referenced for animation
facing = (int)directionsList[0];
animator.SetInteger ("Direction", facing);
}
//This sets the idle animation when you aren't moving, I'm sure someone else could handle this better, I however am not a genius
if (isMoving == false) {
//We will only not have the directionsList set for the opening, but still error handling is important guys!
if (directionsList.Count != 0) {
facing = (int)directionsList[0];
}
//Need to add add 4 to the facing for idle animations
facing = facing + 4;
animator.SetInteger ("Direction", facing);
}
}
}
Are you more concerned about the sprite facing the wrong way, or the fact that pushing two arrow-keys down causes odd behavior?
$$anonymous$$oyae! Thank you for the reply! I'm really only concerned about the sprite facing the wrong way. The movement itself is correct, however the character looks like he's doing a moonwalk when he is facing in the opposite direction he walks.
Answer by Koyae · Feb 05, 2015 at 09:02 PM
Line 80 is probably what's killing you along with the related code which performs setup for it.
It's been a while since I've played the old overhead Zelda games, so I don't know whether diagonal is even an option but if I remember right, it's not. If there's only really ever just up, down, left, or right, what you'll ultimately want to do is cut out any code that deals with diagonal stuff, which includes tracking more than one key at once.
Instead of dealing with directionList
at all, you'll probably want to just ignore any directions that are pressed subsequent to the first one... really coarsely, you might use a structure like this:
public void Update(){
// move character if any movement-keys are down
if (OneOrMoreMoveKeysArePressed){
Key keyThatDoesntGetIgnored;
if (lastImportantKey!=-1){ // -1 would mean we don't have one
keyThatDoesntGetIgnored = lastImportantKey;
// lastImportantKey would be some variable stored per-instance
// that tracks the last key that moved the player the last time
// Update() was called. We fall back to that if there's conflict
// "Age before beauty" and all that.
} else {
keyThatDoesntGetIgnored = keyThatJustGotPressed;
}
doStuff(keyThatDoesntGetIgnored);
} // done moving character
} // done updating
If you're more worried about the just visual of your main character moonwalking around the place, you'll just want to enforce either the sprite updating to the direction of movement of the player, or vice-versa. With a Vector2 it would look something like:
Vector2 vDown, vUp, vLeft, vRight;
vDown = new Vector2(0,-1); // no movement left to right on x, negative movement on y
vRight = new Vector2(1,0); // positive movement on x, no movement on y;
/* ...you get the picture. */
if (PlayerVector.normalized == vDown){
setSprite("down");
} else if (PlayerVector.normalized == vLeft) {
setSprite("left");
} else if {
/* ... */
// etc.
}
It might look slightly different if you had a different way of determining the actual moving direction of your character, but there ought to be some way to discern what's really happening and force your sprite to the right state or frame to match.
More generally speaking, you have a LOT of Update()
code executing sequentially which is not broken up into smaller functions that could deal with smaller more-specific aspects of how you want things to work. Your issues will be easier to find if you have UpdateSprite()
, Move(Key[] pressedKeys)
, SwingSword()
, etc.
Thanks $$anonymous$$oyae! I'm really only worried about the moon walking, if you are thinking back to the original zelda you were correct, it was tile by tile movements with no diagonal, however I'm going more for the super nes version which had diagonal movement. I think your second solution will work great for this. When I get home I'll give it a shot and see if it works and then mark you ad the correct answer if it works.
Also you're right. $$anonymous$$y update is a mess, when I prototype things I tend to just write line by line (years of PHP have given me that bad habit), but I will also clean if up, thanks so much!
Hey $$anonymous$$oyae, after co$$anonymous$$g back to this I came up with:
Vector2 PlayerVector = new Vector2(Input.GetAxisRaw("Horizontal"),Input.GetAxisRaw("Vertical"));
Vector2 vDown, vUp, vLeft, vRight;
vDown = new Vector2(0,-1); // no movement left to right on x, negative movement on y
vUp = new Vector2(0,1);
vRight = new Vector2(1,0); // positive movement on x, no movement on y;
vLeft = new Vector2(-1,0);
if (PlayerVector.normalized == vDown) {
animator.SetInteger ("Direction", 0);
} else if (PlayerVector.normalized == vLeft) {
animator.SetInteger ("Direction", 1);
} else if (PlayerVector.normalized == vUp) {
animator.SetInteger ("Direction", 2);
} else if (PlayerVector.normalized == vRight) {
animator.SetInteger ("Direction", 3);
}
This unfortunately still doesn't solve the issue that I can press two keys away from the player and it will cause him to moonwalk backwards from the original direction he is facing.
Go ahead and print out what PlayerVector.normalized
actually is to the console using Debug.Log(PlayerVector.normalized);
It's possible you're skipping that entire elif block because none of the conditions are satisfied.