- Home /
"For"-Loop only works for final element in array
Update: After playing around with it a little, I discovered that the script seems to only work for the last element in the array. I put three blocks into the array, and even when I switched around their order, whichever one was the third element in the array would be impassible, and the others could be moved through. I still have no clue what the cause of this is.
I also moved the line...
walls = GameObject.FindGameObjectsWithTag("wall");
...into a Start() function, as some comments recommended. This doesn't seem to have changed anything, but I understand why it's better this way. Thanks in advance for any help.
Original Question:
I'm having problems with a For Loop I'm using in a custom "collision" system. I put that word in quotes because I'm implementing kind of grid-based movement, and I'm not using any physics. In my game, I have one "player" block (with dimensions 1x1x1) and multiple "wall" blocks (also with dimensions 1x1x1). The "wall" blocks are static, and the "player" block moves around by increments of 1 unit. Below, I've provided some code I wrote that detects whether the "player" block is next to a "wall" block, and sets a flag that won't allow the "player" block to move in that direction when the arrow key is pressed.
When I have one "wall" block in a scene, the script works fine, and the "player" block cannot move through it. I wanted to have more than one "wall" block, though -- in fact, I want to be able to place as many as I want and have the code apply to them automatically -- so I edited the code to iterate through all instances of "wall" blocks when determining if the "player" block was next to one. To do this, I added a "wall" tag to my "wall" block prefab, and made an array that includes all objects with that tag. This part seems to work fine -- when I start up the game, I can see all the "wall" blocks listed in the array, so they have all been detected successfully.
The problem is this: When there is more than one "wall" block in a scene, only the first "wall" block is impassible. There is no discernable difference between two "wall" blocks aside from their position, and both have the "wall" tag that includes them in the array -- which is being iterated through -- so I thought that both would be impassible. However, while the "player" block cannot move through one "wall" block, the other one allows the "player" to move straight through it, as if the code is ignoring any more than one object.
The full code is below. Any help is much appreciated!
#pragma strict
var player : Transform;
var speed : float = 5;
var walls : GameObject[];
var right_move = true;
var left_move = true;
var up_move = true;
var down_move = true;
function Start() {
walls = GameObject.FindGameObjectsWithTag("wall");
}
function Update() {
//All this determines whether or not the "player" block is on any side of a "wall"
//block and sets respective variables to false if it is.
for (var wall : GameObject in walls) {
if (player.position.x + 1 == wall.transform.position.x
&& player.position.z == wall.transform.position.z)
right_move = false;
else right_move = true;
if (player.position.x - 1 == wall.transform.position.x
&& player.position.z == wall.transform.position.z)
left_move = false;
else left_move = true;
if (player.position.z + 1 == wall.transform.position.z
&& player.position.x == wall.transform.position.x)
up_move = false;
else up_move = true;
if (player.position.z - 1 == wall.transform.position.z
&& player.position.x == wall.transform.position.x)
down_move = false;
else down_move = true;
}
}
while(true) yield CoUpdate();
function CoUpdate() {
if (Input.GetKey(KeyCode.RightArrow)
&& right_move == true)
yield Move(Vector3.right);
if (Input.GetKey(KeyCode.LeftArrow)
&& left_move == true)
yield Move(Vector3.left);
if (Input.GetKey(KeyCode.UpArrow)
&& up_move == true)
yield Move(Vector3.forward);
if (Input.GetKey(KeyCode.DownArrow)
&& down_move == true)
yield Move(Vector3.back);
else
yield;
}
function Move(distance : Vector3) {
var pt = player.transform;
var goal = pt.position + distance;
while (pt.position != goal) {
pt.position = Vector3.MoveTowards(pt.position, goal, speed * Time.deltaTime);
yield;
}
}
Print out the length of 'walls' to make absolutely certain that all the walls objects are in the list. If you are creating it in code, you need to make sure of he order the scripts are executing in order for FindGameObjectsWithTag() to find all of the walls.
As @robertbu said, watch your execution order. You are filling your array outside of any local scope, which I'm pretty sure equates to when the script initialises or Awake() (I believe). Some of the GameObjects you are trying to fill your array with may not exist yet. Trying putting your FindGameObjectsWithTag() method in Start(). :)
Thanks for the responses. I tried moving walls = GameObject.FindGameObjectsWithTag("wall");
into Start(), and even into Update(), but in both situations, only one "wall" block was impassible, and the other seemed to be ignored.
When I playtest the scene, I can keep the "player" object (which has the script on it) in the inspector, and I see the "size" of the walls array increase to whatever number of "wall" blocks I have, so I'm pretty sure it's filling the array. It seems like the problem is with the loop itself...
Answer by whydoidoit · Mar 17, 2013 at 07:25 AM
Well you are comparing floats using an == operator when you check the positions .x .y and .z
Floating points can be compared to 0, but many other values will not be accurate enough for a == test.
The normal approach it to take number a from number b and compare it with a very small value:
if((player.position.z - 1 - wall.transform.position.z) < 0.0001f) { //Do something }
Thanks for posting. I made these changes you mentioned, and while they produced (what I assume is) more robust accuracy in making the "wall" blocks impassible, still only one of the "wall" blocks was affected by the code, and the others could be moved straight through -- and again it was the last block in the array that was impassible. I believe the problem may be in the construction of the loop itself, but I can't imagine what it could be.
The way you are going about this is certainly odd - especially given the accuracy of floats.
You would be better off storing your position in integers and then converting it to a Vector3 to display the objects if you want to use ifs like that.
You could also try using $$anonymous$$athf.RoundToInt() on each of the coordinates...
Thanks, I'll try that stuff out! For the record, I'm pretty new to program$$anonymous$$g and to Unity, so if something I do seems odd, it's probably because it's the only way I know how to do it! If there's a better way to get the same results ("wall" blocks being impassible) then I'd love to hear it.
Your answer
Follow this Question
Related Questions
have array target person of most priority/stick with top prior 0 Answers
Ways to optimize this code? iterate through inventory 0 Answers
Javascript for loops and Arrays. 2 Answers
Instantiate object in for loop with array 2 Answers
Making an Array of Animations Play at Random With No Repeats 3 Answers