- Home /
[Long Post] Constant linear movement in a 2d array grid with auto-turning on detecting a wall
I'm using a 2d array grid for my level with each grid assigned a value of either 0 (walkable grid) or 1 (unwalkable grid/wall). Every time an input button is pressed, it initiates a continuous movement in that respective direction, until it is supposed to turn to its' right. This is pretty reminescent of Snake or Pacman. I have managed to get the gist of the movement to work, however it is not optimized. The straight direction that it moves in, happens very fast. The object stops when it comes against an unwalkable grid instead of turning. Only when the input key is pressed then, that it detects the unwalkable grid and takes a turn. If possible, it would be great if someone could take a look at my project HERE
or if you wish to check my code, here it is
using UnityEngine;
using System.Collections;
using DG.Tweening;
using System.Collections.Generic;
public class Player : MonoBehaviour {
const float TweenWpDuration = 0.2f;
public GameObject levelCreator_;
levelCreator temp;
Vector3 playerPos;
int[,] mapArray = new int[13,17];
public bool inhibitPlayerInput;
void Start () {
DOTween.Init(true, true, LogBehaviour.Verbose).SetCapacity(6000, 6000);
levelCreator_ = GameObject.Find ("LevelCreatorGameObject");
temp = levelCreator_.gameObject.GetComponent<levelCreator>();
mapArray = temp.mapArray;
for(int i = 1; i<12; i++)
{
for(int ii = 1; ii < 16; ii++)
{
if( mapArray[i,ii] == 2)
{
playerPos = new Vector3(i,ii,0);
}
}
}
Debug.Log (GhostManager.ghostTweens.Count);
transform.position = new Vector3(playerPos.x,playerPos.y,0);
}
void Update () {
if (inhibitPlayerInput) return;
getInput();
}
void getInput()
{
bool inputPressed = false;
if(Input.GetKey(KeyCode.W) && inhibitPlayerInput == false)
{
inputPressed = true;
walkup();
}
else if (Input.GetKey(KeyCode.S))
{
inputPressed = true;
walkdown();
}
else if(Input.GetKey(KeyCode.A))
{
inputPressed = true;
walkleft();
}
else if(Input.GetKey(KeyCode.D))
{
inputPressed = true;
walkright();
}
}
void walkup ()
{
Vector3 newPlayerPos = playerPos;
newPlayerPos += new Vector3(-1,0,0);
if (mapArray[(int)newPlayerPos.x,(int)newPlayerPos.y] == 1)
{
if (mapArray[(int)playerPos.x,(int)playerPos.y + 1] == 0 || mapArray[(int)playerPos.x,(int)playerPos.y + 1] >= 2)
walkright();
else
walkleft();
return;
}
playerPos = newPlayerPos;
if(mapArray[(int)playerPos.x,(int)playerPos.y] == 0 || mapArray[(int)playerPos.x,(int)playerPos.y] >= 2)
{
inhibitPlayerInput = true;
transform.DOMove(playerPos, TweenWpDuration).OnComplete(() => inhibitPlayerInput = false).SetEase(Ease.Linear);
}
if (mapArray[(int)playerPos.x - 1,(int)playerPos.y] == 0 || mapArray[(int)playerPos.x - 1,(int)playerPos.y] >= 2)
{
playerPos = newPlayerPos;
walkup();
}
return;
}
void walkright ()
{
Vector3 newPlayerPos = playerPos;
newPlayerPos += new Vector3(0,1,0);
if (mapArray[(int)newPlayerPos.x,(int)newPlayerPos.y] == 1)
{
if (mapArray[(int)playerPos.x + 1,(int)playerPos.y] == 0 || mapArray[(int)playerPos.x + 1,(int)playerPos.y] >= 2)
walkdown();
else
walkup();
return;
}
playerPos = newPlayerPos;
if(mapArray[(int)playerPos.x,(int)playerPos.y] == 0 || mapArray[(int)playerPos.x,(int)playerPos.y] >= 2)
{
inhibitPlayerInput = true;
transform.DOMove(playerPos, TweenWpDuration).OnComplete(() => inhibitPlayerInput = false).SetEase(Ease.Linear);
}
if (mapArray[(int)playerPos.x,(int)playerPos.y + 1] == 0 || mapArray[(int)playerPos.x,(int)playerPos.y + 1] >= 2)
{
playerPos = newPlayerPos;
walkright();
}
return;
}
void walkleft ()
{
Vector3 newPlayerPos = playerPos;
newPlayerPos += new Vector3(0,-1,0);
if (mapArray[(int)newPlayerPos.x,(int)newPlayerPos.y] == 1)
{
if (mapArray[(int)playerPos.x + 1,(int)playerPos.y] == 0 || mapArray[(int)playerPos.x + 1,(int)playerPos.y] >= 2)
walkdown();
else
walkup();
return;
}
playerPos = newPlayerPos;
if(mapArray[(int)playerPos.x,(int)playerPos.y] == 0 || mapArray[(int)playerPos.x,(int)playerPos.y] >= 2)
{
inhibitPlayerInput = true;
transform.DOMove(playerPos, TweenWpDuration).OnComplete(() => inhibitPlayerInput = false).SetEase(Ease.Linear);
}
if (mapArray[(int)playerPos.x,(int)playerPos.y - 1] == 0 || mapArray[(int)playerPos.x,(int)playerPos.y - 1] >= 2)
{
playerPos = newPlayerPos;
walkleft();
}
return;
}
void walkdown ()
{
Vector3 newPlayerPos = playerPos;
newPlayerPos += new Vector3(1,0,0);
if (mapArray[(int)newPlayerPos.x,(int)newPlayerPos.y] == 1)
{
if (mapArray[(int)playerPos.x,(int)playerPos.y + 1] == 0 || mapArray[(int)playerPos.x,(int)playerPos.y + 1] >= 2)
walkright();
else
walkleft();
return;
}
playerPos = newPlayerPos;
if(mapArray[(int)playerPos.x,(int)playerPos.y] == 0 || mapArray[(int)playerPos.x,(int)playerPos.y] >= 2)
{
inhibitPlayerInput = true;
transform.DOMove(playerPos, TweenWpDuration).OnComplete(() => inhibitPlayerInput = false).SetEase(Ease.Linear);
}
if (mapArray[(int)playerPos.x + 1,(int)playerPos.y] == 0 || mapArray[(int)playerPos.x + 1,(int)playerPos.y] >= 2)
{
playerPos = newPlayerPos;
walkdown();
}
return;
}
}
A hint for your code: replace walkup/down/right/left with a walk-method, that takes a Vector3, and use that Vector3 to give directions.
Now you have four methods that are identical with the exception of some coordinates. That means that whenever you want to change your walk-methods, you have to change all of them, and make sure that you change exactly the same thing every time. I can guarantee that you will mess up doing that, trust me.
If you do that and repost your code, it will be much easier for us (and you) to find the problems.
DRY code is worth ai$$anonymous$$g for. Could certainly reduce this long post to a short one.
Thanks for the replies, @Baste and @Bored$$anonymous$$ormon.
Actually I wanted to have a single method as well, but I'm not aware of how to code such a behavior :( I believe that the discrepencies are arising because of some logic in these four methods that I currently have.
I apologize for not being able to edit the code and hereby assist users here to help me find the problem. I've been stuck on this exact issue for 3 weeks now despite posting on various different forums.
To be a bit more clear, below is the previous version of my code, which allows for movement to happen only when a key is pressed. If no key is pressed, the movement doesn't happen anymore.
public GameObject levelCreator_;
levelCreator temp;
Vector3 playerPos;
int[,] mapArray = new int[13,17];
public bool inhibitPlayerInput;
void Start () {
DOTween.Init(true, true, LogBehaviour.Verbose).SetCapacity(6000, 6000);
levelCreator_ = GameObject.Find ("LevelCreatorGameObject");
temp = levelCreator_.gameObject.GetComponent<levelCreator>();
mapArray = temp.mapArray;
for(int i = 1; i<12; i++)
{
for(int ii = 1; ii < 16; ii++)
{
if( mapArray[i,ii] == 2) // tile with value 2 is the starting position of the player
{
playerPos = new Vector3(i,ii,0);
}
}
}
transform.position = new Vector3(playerPos.x,playerPos.y,0);
}
void Update () {
if (inhibitPlayerInput) return;
getInput();
}
void getInput()
{
bool inputPressed = false;
Vector3 newPlayerPos = playerPos;
if(Input.Get$$anonymous$$ey($$anonymous$$eyCode.W))
{
inputPressed = true;
newPlayerPos += new Vector3(-1,0,0);
}
else if (Input.Get$$anonymous$$ey($$anonymous$$eyCode.S))
{
inputPressed = true;
newPlayerPos += new Vector3(1,0,0);
}
else if(Input.Get$$anonymous$$ey($$anonymous$$eyCode.A))
{
inputPressed = true;
newPlayerPos += new Vector3(0,-1,0);
}
else if(Input.Get$$anonymous$$ey($$anonymous$$eyCode.D))
{
inputPressed = true;
newPlayerPos += new Vector3(0,1,0);
}
if (!inputPressed) return;
if (mapArray[(int)newPlayerPos.x,(int)newPlayerPos.y] == 1) // Unwalkable tile check
{
return;
}
playerPos = newPlayerPos;
if(mapArray[(int)playerPos.x,(int)playerPos.y] == 0 || mapArray[(int)playerPos.x,(int)playerPos.y] >= 2)
{
inhibitPlayerInput = true;
transform.DO$$anonymous$$ove(playerPos, TweenWpDuration).OnComplete(() => inhibitPlayerInput = false).SetEase(Ease.Linear);
return;
}
}
Answer by Baste · Oct 03, 2014 at 01:23 PM
I found your walking really fast problem! At the end of your move methods, if you can keep moving in that direction, you call the move method again.
So in the end of moveUp, you call moveUp. This should cause the player to move as far as it can upwards instantly. Instead of doing that, you should do this:
Have a variable remembering which input the player pressed last.
At a regular interval (say .5 seconds), move the player in the last input direction.
When the player hits a wall, change the input to either right or left
This is a code suggestion, make note that I haven't tested it, as I don't have access to all of your code. It does the three points above, and I've refactored your walkNorth/South/East/West into one walk method. Feel free to ask questions. It won't work out of the box, but it should be a starting point to get you in the right direction.
public class Player : MonoBehaviour {
const float TweenWpDuration = 0.2f;
public GameObject levelCreator_;
levelCreator temp;
Vector3 playerPos;
int[,] mapArray = new int[13, 17];
public bool inhibitPlayerInput;
//STORE INPUT!
Vector3 currentMovementFront, currentMovementRight, currentMovementLeft, currentMovementBack;
Vector3 upVector = new Vector3(-1, 0, 0);
Vector3 downVector = new Vector3(1, 0, 0);
Vector3 rightVector = new Vector3(0, 1, 0);
Vector3 leftVector = new Vector3(0, -1, 0);
void Start() {
//start moving up
currentMovementFront = upVector;
currentMovementRight = rightVector;
currentMovementLeft = leftVector;
currentMovementBack = downVector;
StartCoroutine(MovePlayer());
}
//moves the player every half second
IEnumerator MovePlayer() {
while (true) {
yield return new WaitForSeconds(.5f);
if (!inhibitPlayerInput)
walk(currentMovementFront, currentMovementRight, currentMovementLeft, currentMovementBack);
}
}
void Update() {
if (inhibitPlayerInput)
return;
getInput();
}
void getInput() {
if (!inhibitPlayerInput) {
if (Input.GetKey(KeyCode.W)) {
SetCurrentMovement(upVector, rightVector, leftVector, downVector);
}
else if (Input.GetKey(KeyCode.S)) {
SetCurrentMovement(downVector, leftVector, rightVector, upVector);
}
else if (Input.GetKey(KeyCode.A)) {
SetCurrentMovement(leftVector, downVector, upVector, rightVector);
}
else if (Input.GetKey(KeyCode.D)) {
SetCurrentMovement(rightVector, upVector, downVector, leftVector);
}
}
}
private void SetCurrentMovement(Vector3 front, Vector3 right, Vector3 left, Vector3 down) {
currentMovementFront = front;
currentMovementRight = right;
currentMovementLeft = left;
currentMovementBack = down;
}
void walk(Vector3 movement, Vector3 relativeRight, Vector3 relativeLeft, Vector3 relativeBack) {
Vector3 newPlayerPos = playerPos;
//newPlayerPos += new Vector3(-1, 0, 0);
newPlayerPos += movement;
if (mapArray[(int)newPlayerPos.x, (int)newPlayerPos.y] == 1) {
Vector3 rightPos = playerPos + relativeRight;
if (mapArray[(int)rightPos.x, (int)rightPos.y] == 0 || mapArray[(int)rightPos.x, (int)rightPos.y] >= 2)
walk(relativeRight, relativeBack, movement, relativeLeft);
else
walk(relativeLeft, movement, relativeBack, relativeRight);
return;
}
playerPos = newPlayerPos;
if (mapArray[(int)playerPos.x, (int)playerPos.y] == 0 || mapArray[(int)playerPos.x, (int)playerPos.y] >= 2) {
inhibitPlayerInput = true;
transform.DOMove(playerPos, TweenWpDuration).OnComplete(() => inhibitPlayerInput = false).SetEase(Ease.Linear);
}
if (mapArray[(int)playerPos.x - 1, (int)playerPos.y] == 0 || mapArray[(int)playerPos.x - 1, (int)playerPos.y] >= 2) {
playerPos = newPlayerPos;
//walkup(); //This recursive call caused really fast movement
}
return;
}
}
Thanks for the quick reply @Baste. That code seems to work. I needed to add the following code inside Start in order to place the player object inside the array grid.
levelCreator_ = GameObject.Find ("LevelCreatorGameObject");
temp = levelCreator_.gameObject.GetComponent<levelCreator>();
mapArray = temp.mapArray;
for(int i = 1; i<12; i++){
for(int ii = 1; ii < 16; ii++){
if( mapArray[i,ii] == 2){
playerPos = new Vector3(i,ii,0);
}
}
}
transform.position = new Vector3(playerPos.x,playerPos.y,0);
However the movement doesn't move with a smooth linear speed. Currently, it moves, and pauses, and moves, and pauses, each time it reaches a new cell. I changed the WaitForSeconds from .5f to .005f, to make the movement smoother, but that makes the input virtually disabled. Also, sometimes when input is pressed in the direction of a wall, ins$$anonymous$$d of continuing in its' current direction, the player object moves in reverse. To better understand this, kindly check the project linked here (390kb) - ProjectLink Also, there seems to be some glitch when if the object goes to a corner, it keeps doing a to and fro oscillatory movement in the adjacent tiles in that corner and gets stuck in some loop. A video grab of the same can be found Here(80kbs)
The move/pause is due to you shifting the transform's position directly to a spot.
The correct way to move a thing is to set a destination, and then move the thing over time towards that destination - usually combining Vector3.$$anonymous$$oveTowards to get the next position and Time.deltaTime to make the movement framerate independent.
Your answer
Follow this Question
Related Questions
Touch and Drag 3D Objects on a Grid (for Android) 0 Answers
Push into Two-Dimensional Array 2 Answers
Access a specific element in GridLayoutGroup by script ? 1 Answer
help with direction 1 Answer