Continuously spawn 4 squares on top of each other regardless of speed - c#

I have a function that spawns 4 objects in a row and those objects move down.I want to keep spawning 4 objects on top of previous 4 objects, but I want to avoid having empty horizontal space between those rows. Think of it like a chess board or any board with squared scrolling downwards indefinitely, and the 4 objects that I keep spawning are chess board squares.
This is my current function that spawns 4 objects in horizontal and I have a coroutine that calls this in random intervals
public void CreateLetter()
{
var wordLetters = randomChosenWord.ToCharArray();
for (int i = 0; i < letterPlacements.Length; i++)
{
var chosenLetter = wordLetters[Random.Range(0, wordLetters.Length)];
//Do not spawn too much duplicates
var numberOfSameLetters = FindObjectsOfType<LetterController>().Where(l => l.Letter == chosenLetter).ToList().Count;
var lettersAlreadyChosen = FindObjectOfType<PlayerControls>().playerChosenLetters;
//Did player choose any letter already? spawn that letter less
if (numberOfSameLetters > (lettersAlreadyChosen.IndexOf(chosenLetter) != -1 ? 1 : 2))
{
var trimmedRandomWord = randomChosenWord.Trim(chosenLetter);
chosenLetter = trimmedRandomWord[Random.Range(0, trimmedRandomWord.Length)];
}
GameObject letter = Instantiate(letterPrefab);
letter.transform.position = new Vector2(letterPlacements[i].transform.position.x, letterPlacements[i].transform.position.y + 1);
letter.GetComponent<SpriteRenderer>().sprite = letterSprites[alphabet.IndexOf(char.ToUpper(chosenLetter))];
letter.GetComponent<LetterController>().Letter = chosenLetter;
}
}
Should make a check if the new four square positions would collide with previous four? Any ideas or suggestions?

After some digging and calculating, I found out that what I need actually is called
inverse proportion.
To make the coroutine spawn 4 squares after the previous 4 have spawned (so that they are perfectly aligned), I use the movement speed of squares and then calculate the time when the coroutine should run again and spawn another 4 squares. In other words, the bigger the movement speed, the faster the coroutine function repeats:
IEnumerator GenerateLetters()
{
while(randomChosenWord.Length > 0)
{
yield return new WaitForSeconds(1 / (movementSpeed * 0.5f));
CreateLetter();
}
}
and this is the Update function in script attached to square prefab object:
// Update is called once per frame
void Update()
{
//Move letter downwards
transform.position = new Vector2(transform.position.x, transform.position.y - movementSpeed * Time.deltaTime / 2);
gameObject.GetComponentInChildren<TextMesh>().text = Letter.ToString();
}

Related

Instantiate blocks evenly between two instantiated objects

I'm instantiating obstacles at random X seconds and they fall down the screen. I was looking for a way to instantiate blocks in between the falling obstacles.
Here’s the falling obstacle:
GIF Example
IEnumerator ObstacleSpawner()
{
while (true)
{
obstacleSpawn = Random.Range(0.25f, 0.9f);
yield return new WaitForSeconds(obstacleSpawn);
GameObject newObstacle = Instantiate(obstacle, new Vector2(-1, 6.5f), Quaternion.identity);
}
}
And this is what I’m looking for:
Example
I’m looking for two things.
To have the blocks created if the falling objects are within a certain distance. So in my code, if the obstacleSpawn time is between 0.25 – 0.6 secs, the block wont spawn. After that, the larger the obstacleSpawn time, the more blocks created.
To have the block spawn evenly in between the obstacles. If one block is instantiated, its in the middle of the two obstacles. If its two, three and so on, they’re spread out evenly as the above pic shows.
I’ve tried to do this using the following code but obviously with this, it’s instantiating every X secs as I’m a beginner and I honestly don’t know where to start to do this, so would appreciate some help.
IEnumerator BlockSpawner()
{
while (true)
{
yield return new WaitForSeconds(Random.Range(0.25f, 0.9f));
if (obstacleSpawn >= 0.5f && obstacleSpawn < 0.7f)
{
GameObject newBlock = Instantiate(block, new Vector2(Random.Range(-1.65f, 1.65f), 6.5f), Quaternion.identity);
}
else if (obstacleSpawn >= 0.7f && obstacleSpawn < 0.8f)
{
for (int i = 0; i < 1; i++)
{
GameObject go = Instantiate(Instantiate(block));
go.transform.position = new Vector2(Random.Range(-1.65f, 1.65f), 6.5f);
}
}
else if (obstacleSpawn >= 0.8f && obstacleSpawn < 0.9f)
{
for (int i = 0; i < 2; i++)
{
GameObject go = Instantiate(Instantiate(block));
go.transform.position = new Vector2(Random.Range(-1.65f, 1.65f), 6.5f);
}
}
else if (obstacleSpawn >= 0.9f)
{
for (int i = 0; i < 3; i++)
{
GameObject go = Instantiate(Instantiate(block));
go.transform.position = new Vector2(Random.Range(-1.65f, 1.65f), 6.5f);
}
}
}
}
Hopefully I am getting your question right please add more detail if I am wrong, but right now your variable "obstacleSpawn" is a random time and then the less this variable is, the closer together the objects are spawning. So you are using time to measure the distance between the objects. This works well but since you want to spawn the objects evenly this makes things a bit more complicated.
The easiest way is probably to just mess around with the y position of the red blocks while they spawn until you get it right. This would look something like -
for (int i = 0; i < 2 i++)
{
GameObject go = Instantiate(Instantiate(block));
go.transform.position = new Vector2(Random.Range(-1.65f, 1.65f), newObstacle.transform.postition.y + (1 * i));
}
Now you would need a reference to the newObstacle from your other Coroutine, or you could change the "newObstacle" gameobject/variable's scope to be the entire script (declare the variable at the top and change it in ObstacleSpawner() if that makes sense). Then you can just play around with the 1 in "(1 * i)" to get different spacing.
I recommend this ^ but I will explain another way too -
If you really want to use the obstacles spawning, and divide the distance between them to get exact measurements you can, but it'll be more complicated. You would need a reference both to the most recent spawned obstacle (say object1), and the second most recent (say object2), then you could divide their y distance by however many blocks you have, say if you have 3 blocks you want to spawn - ((object1.transform.position.y - object2.transform.position.y) / 3) - this will return the increment of y each block will be, you could then substitute the (1 * 1) from before with this value.
Edit - More explanation for second way -
The easiest way would be to make a list of the obstacles you spawn and then use the last 2 obstacles in the list and calculate the distance between them.
To add the list, at the top of your script you would add something that would look like
private List<GameObjects> listOfObstacles;
Then in the ObstacalSpawner() function you would add the gameobject to that list "listOfObjects.Add(newObstacle)", this will create a list of the obstacles from first spawned to last (most recent). Now you can use the way I said before to split them up -
float distanceBetweenBlocks = ((listOfObjects[listOfObjects.Count - 2].transform.position.y - listOfObjects[listOfObjects.Count - 1].transform.position.y) / 3)
go.transform.position = new Vector2(Random.Range(-1.65f, 1.65f), listOfObjects[listOfObjects.Count - 2].transform.postition.y + (distanceBetweenBlocks * i));
the "listOfObjects[listOfObjects.Count - 1]" will get you the last item in that list (most recently spawned object) then -1 to the length will get you the second to last, then you just do what I said before, getting the distance between each and dividing it by however many blocks you want (you will want to change the 3 to however many blocks you want)
Hopefully that clears up how that works and it makes sense. Good Luck!

Knocking back the Player in a Unity 2D TopDown Game

When something hits the Player, maybe an enemy or spikes etc., I want him to get knocked back in the oppisite direction of the enemy.
So what I already got:
public void ChangeHealth(float healthToAdd, Vector2 objectPosition) // Change the players health, objectPosition = enemies position or something else
{
if (healthToAdd < 0) // incoming damage
{
// ... other stuff
Knockback(objectPosition);
}
// ... other stuff
}
void Knockback(Vector2 objectPosition) // Knockback routine
{
Vector2 knockbackPosition = new Vector2( , ); // calculation is missing here! Calculate the new position by the knockback direction
rigid.MovePosition(Vector2.MoveTowards(playerPos, knockbackPosition, 2 * Time.deltaTime)); // the knock back
}
private void Update() // !! TEST !!
{
if (Input.GetKeyDown(KeyCode.E)) // TEST routine
{
ChangeHealth(-7, new Vector2(10,10)); // decrease players health by 7 and knock him back
}
}
And what is missing:
Vector2 knockbackPosition = new Vector2( , );
I am looking for a calculation like this picture is showing:
Vector2 knockbackPosition =
transform.position + (transform.position - objectPosition).normalized *a
To understand why is it equal to that you have to read through.
There are three points: E,P,K (Enemy, Player, Knockback)
And one scalar number: a (the greater this value is, the more knockback you'll have)
Now from your picture:
PK = EP*a
expand vectors into distances between two points:
(K-P) = (P-E)*a
calculate the location of K:
K = P + (P-E)*a
There is one problem though. (thanks to Rotem) with this formula as it is:
you'd expect a bigger knockback from a closer opponent.
We don't want the knockback to be dependent on the distance between P and E.
To remove the dependency on the former, normalize the vector before multiplying by a
So we add .normalized to use just the direction of (P-E) instead of its original vector

XNA Tile based Game: Checking collision between 2 objects using their grid position inside a 2D array

I was wondering if anyone could help me figure out how to check collisions between 2 objects inside a 2d array.
I'm trying to make a simple 2D top down game. The map is made up of a 20x20 2D array of squares. The player and the enemies are all squares and each take up one square inside the grid. The grid is made up of multiple squares of different types. So far I have floor squares(which the player and enemies will be allowed to move along), Wall squares(Which the player and enemies cannot move past or along) and then the enemy and players squares. Here is a screenshot of it:
I create and initialise the 2D array in the Game Class. Using numbers from 0-3 I can select what each square will contain:
0- Floor square 1- Wall square 2- Enemy Square 3- player Square.
So firstly I have the player colliding with the wall squares by simply using a nested for loop and checking the grid positions Above, Below, Left and Right of the player to see if the the value within the 2D array is 1. If it isn't then that means it's not a wall which means the player is allowed to move.
However I can't use this method when checking collision between the player and enemy as I only use the value inside the 2D array(2 and 3) to choose where to originally draw the enemy and player squares. This means that the grid position that they are both currently on at any time contains a value of 0.
I thought I would go about it by storing the the "grid Position" of each player and enemy object. That way I could just check each grid block around the player to see if it was equal to the grid position of an enemy. If it was equal then I would set the movement vector that I use to move the player in that specific direction to (0,0) preventing them from moving in that direction. When the enemy is no longer within one block of the player then the movement vector is given back it's original value.
I've had some success with this but it only seems to work with one enemy object. With one enemy I can't pass through it from any angle but with the others I can pass right through them.
Note: It seems that the enemy furthest down the screen is the only enemy that the player square can collide with.
I've used break points and I can see that when I get close to any of the enemies I can pass through, it does actually run the check to see if the square next to the player is an enemy but it doesn't actually prevent the player from walking through the enemy.
So my question is, am I going about doing the collision wrong, is there a simpler way to do it? Or if perhaps there may be a mistake in my code which is stopping it from working?
Below is some of my code:
This first sample is for the player/enemy Collision. This is contained in the enemy class. As I stated before, I use the player and enemies grid position to check to see if they are within one square of each other, if they are then I set the players movement vector to 0,0. When they are no longer within one square of each other I reset the movement vector back to it's original value.
public void CheckCollision(Player playerObject)
{
//Check above player
if (playerObject.PlayerGridPosition.Y - 1 == gridPosition.Y && playerObject.PlayerGridPosition.X == gridPosition.X)
{
//north is a vector2 variable that I use to add to the players position in order to move them about the screen. I use it to update both the players position
//and the grid position. North, South, East and West are all similar except the values contained in each are for a specific direction
playerObject.north.X = 0;
playerObject.north.Y = 0;
//This bool is used to check for when an enemy is beside and no longer beside the player.
besidePlayer = true;
}
//Check below player
if (playerObject.PlayerGridPosition.Y + 1 == gridPosition.Y && playerObject.PlayerGridPosition.X == gridPosition.X)
{
playerObject.south.X = 0;
playerObject.south.Y = 0;
besidePlayer = true;
}
//Check to right of player
if (playerObject.PlayerGridPosition.Y == gridPosition.Y && playerObject.PlayerGridPosition.X + 1 == gridPosition.X)
{
playerObject.east.X = 0;
playerObject.east.Y = 0;
besidePlayer = true;
}
//This if statement just checks to see if any of the enemies are within a squares space of the player, if they are not then the besidePlayer bool is set to false
else if (playerObject.PlayerGridPosition.Y != gridPosition.Y && playerObject.PlayerGridPosition.X + 1 != gridPosition.X && playerObject.PlayerGridPosition.Y - 1 != gridPosition.Y && playerObject.PlayerGridPosition.X != gridPosition.X && playerObject.PlayerGridPosition.Y + 1 != gridPosition.Y && playerObject.PlayerGridPosition.X != gridPosition.X)
{
besidePlayer = false;
}
//When an enemy is no longer beside the player then we can reset all the North, South, East and West vector velues back to their original values.
if (besidePlayer == false)
{
playerObject.north.X = 0;
playerObject.north.Y = -1;
playerObject.south.X = 0;
playerObject.south.Y = 1;
playerObject.east.X = 1;
playerObject.east.Y = 0;
}
}
This next piece of code is for where I set the values inside the 2D array and create the layout of the level. It's also where I create the enemy objects and set the player objects position and grid position, using the row,col of their specific "number" on the grid in order to choose where to draw them.
Note: The rows and columns are flipped as otherwise the level gets drawn sidewards.
public void LoadLevels(int level)
{
//More levels will be added in later.
if(level == 1)
{
//Here I set the values inside the 2D array "grid". It is a 20x20 array.
//0 = Floor Square, 1 = Wall square, 2 = Enemy Square, 3 = Player Square
grid = new int[maxRows, maxCols]
{
{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},
{1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1},
{1,0,1,0,0,0,0,1,0,1,1,0,0,0,0,0,0,0,0,1},
{1,0,1,0,1,1,0,1,0,1,1,0,1,1,1,1,1,1,0,1},
{1,0,1,2,1,1,0,1,0,1,1,0,0,0,0,0,0,0,0,1},
{1,0,1,0,1,1,0,1,0,0,0,0,1,1,1,0,1,1,1,1},
{1,0,1,0,1,1,0,1,0,1,1,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,1,1},
{1,1,1,0,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,1},
{1,0,0,0,0,0,0,0,0,1,1,1,0,1,1,1,1,1,0,1},
{1,1,1,1,1,1,1,1,0,0,0,3,0,0,0,2,0,0,0,1},
{1,0,1,0,0,0,0,1,0,1,0,1,0,1,0,1,1,1,0,1},
{1,2,1,0,1,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1},
{1,0,1,0,1,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1},
{1,0,1,2,1,1,0,1,0,1,0,1,0,1,0,0,0,0,0,1},
{1,0,1,0,1,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1},
{1,0,1,0,1,1,0,1,0,0,0,1,0,1,0,1,0,1,0,1},
{1,0,0,0,0,0,0,0,0,1,0,1,0,1,0,1,1,1,0,1},
{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}
};
//Cycle through the array with a nested if
for (int i = 0; i < maxCols; i++)
{
for (int j = 0; j < maxRows; j++)
{
//If the the value at grid[i, j] is a 2 then create an enemy at that position
if (grid[i, j] == 2)
{
enemyList.Add(new Enemies(Content.Load<Texture2D>("redSquare"), new Vector2(j * squareWidth, i * squareHeight), new Vector2(j, i)));
//Reset the value at this position to 0 so that when the player/enemy moves from this spot, a floor tile will be drawn instead of a blank space.
grid[i, j] = 0;
}
//If the value is 3 then set the players position and grid position to the value based of [i, j] values.
if (grid[i, j] == 3)
{
playerObject.PlayerPosition = new Vector2(i * squareWidth, j * squareHeight);
playerObject.PlayerGridPosition = new Vector2(i, j);
grid[i, j] = 0;
}
}
}
}
if (level == 2)
{
}
}
However I can't use this method when checking collision between the
player and enemy as I only use the value inside the 2D array(2 and 3)
to choose where to originally draw the enemy and player squares.
Perhaps this exposes a flaw in your design that you should fix rather than work around. I don't have enough of the code to tell exactly, but it seems like you are duplicating information inside the Player class or perhaps another grid. Based on your description, a 2D grid containing values 0-1-2-3 for each square fully describes the state of the game. Why not just use that Grid as the single source of truth for everything?
It's hard to find where the actual bug without seeing the complete code & debug it directly, but I feel that your approach is too complicated.
Personally, I will follow the following logic/pseudocode:
loading () {
load level to array, no need to perform additional modification
}
game_update () {
for each tile within the array {
if ( tile is player ) {
read user's input
temp <- compute the next position of the player
if ( temp is floor ) then { current player position <- temp } // move allowed, update its position
else { the player must stay on its current position } // move rejected
}
if ( tile is enemy ) {
temp <- compute the next position of this enemy
if ( temp is floor ) then { this enemy position <- temp } // move allowed, update its position
else { this enemy must stay on its current position } // move rejected
}
}
}
game_draw () {
for each tile within the array {
draw the current tile
}
}
This way, any ilegal move will be blocked before it occurs.
Side note: You seem to be missing code for west. If that is unintentional, it should be an easy fix.
I think what may be happening is as soon it one enemy isn't next to the player, it allows the player to walk in all directions again, regardless of restrictions put on by earlier monsters.
If I am correct what is happening is it checks every enemy in descending screen position and blocks the player if then needed. However, if any of the remaining enemies aren't next to the player, the change is undone when the check for that enemy is preformed and the player is allowed to walk through again. This doesn't happen for the last enemy, because their are no more non-adjacent enemies to undo its blocking.
Try changing it so that it resets the allowed movements at the beginning of every frame, as opposed to every time it finds a monster not next to the player.

Source, Target, and Intermediate Way Points

Consider two points: (0,0,0) as source and (1000,0,0) as target
A cube game object wants to travel from source and target at a pre-defined/constant speed. Time taken: t1
Introduce 100 intermediate points between source and target, i.e. INTERMEDIATE_POINTS = 10
Example: (0,0,0), (10,0,0), (20,0,0), (30,0,0).... (980,0,0), (990,0,0), (1000,0,0). Same speed, time taken: t2.
Introduce 50 intermediate points, i.e. INTERMEDIATE_POINTS = 20 ; (0,0,0), (20,0,0), (40,0,0),..., (960,0,0), (980,0,0), (1000,0,0). Same speed, time taken: t3.
Result: t1 < t3 < t2, i.e. more intermediate points, more time taken to reach the target (although same path and same speed)
Question: If you compare, the game object moves in the same way (same path, same speed) in all the three cases (no intermediates, 100 intermediates, and 50 intermediates) that are mentioned above. But why is there a time difference to reach the target?
Code to test this scenario:
using UnityEngine;
using System.Collections.Generic;
public class TestSpeed : MonoBehaviour
{
private List<Vector3> listOfPoints = new List<Vector3>();
private int INTERMEDIATE_POINTS = 1;
private int counter = 1;
private float speed = 50.0f;
private float originalDistance = 0.0f;
private float distanceCovered = 0.0f;
private float overshoot = 0.0f;
private Vector3 modifiedTarget;
// for the car movement.
private Vector3 targetPosition; // after every loop, get the next position
private Vector3 currentPosition;
// Use this for initialization
void Start()
{
for (int i = 0; i <= 1000; i = i + INTERMEDIATE_POINTS)
{
listOfPoints.Add(new Vector3(i, 0, 0));
}
currentPosition = this.transform.position; // at the beginning, from (0,0,0)
targetPosition = listOfPoints[counter];
}
// Update is called once per frame
void Update()
{
originalDistance = Vector3.Distance(targetPosition, currentPosition);
distanceCovered = Vector3.Distance(transform.position, currentPosition);
if(Vector3.Distance(transform.position, new Vector3(0,0,0)) >= 995.0f)
{
System.TimeSpan t = System.TimeSpan.FromSeconds(Time.timeSinceLevelLoad);
string answer = string.Format("{0:D2}:{1:D2}:{2:D2}",
t.Hours,
t.Minutes,
t.Seconds);
}
if ((originalDistance - distanceCovered) <= 0.0f)
{
currentPosition = transform.position;
targetPosition = listOfPoints[counter];
counter++;
}
else
{
float step = speed * Time.deltaTime;
if((distanceCovered + step) >= originalDistance)
{
overshoot = distanceCovered + step - originalDistance;
counter++;
modifiedTarget = Vector3.Lerp(targetPosition, listOfPoints[counter], (overshoot / originalDistance));
}
else
{
modifiedTarget = targetPosition;
}
transform.position = Vector3.MoveTowards(transform.position, modifiedTarget, step);
}
}
}
How to use the code:
Just create a cube game object and assign the script to it. Near to string answer set a break-point to check the time duration with various number of intermediate points.
I'm pretty sure this logic here is the cause of the strange observation:
if ((originalDistance - distanceCovered) == 0.0f)
{
currentPosition = transform.position;
targetPosition = listOfPoints[counter];
counter++;
}
You check whether or not you've arrived at your destination waypoint by checking for an exact position match; however, the distance you travel per Update is anything but exact. That means that your object could very well overshoot the destination, then try to move back towards it, overshoot it again, then repeat.
I bet if you watch your cube in the scene view, you'll see it hover around a single waypoint for a bit until it manages to hit the exact distance it needed.
You're probably better off using an inequality here, for example:
if ((originalDistance - distanceCovered) <= 0.0f)
{ /* ... */ }
Your object has reached its waypoint if the distance it has traveled is greater than or equal to the distance it needed to travel. originalDistance - distanceCovered will be negative as soon as the object has reached or passed the waypoint.
EDIT:
X.....X.....X.....X.....X.....X
Here are some waypoints. Pretend we have an object traveling along the path of waypoints. It starts at the first one on the left and goes right. It'll be represented by an O.
O.....X.....X.....X.....X.....X
Now it moves along for a while. Due to the variability of Time.deltaTime, it might move one or two spots each tick. So let's say it winds up here after a few ticks:
X....OX.....X.....X.....X.....X
And during the next tick, it moves two:
X.....XO....X.....X.....X.....X
With your original check, the object will now travel backwards. It needed to travel a distance of seven spaces, but it traveled 8. So with your original check, originalDistance - distanceCovered != 0.0f. So it'll keep trying to hit that spot over and over again until it hits it on the dot.
Even if you introduce the idea of a "threshold", you're still going to have the same problem. There is no fixed distance traveled per tick, so that means that each waypoint will have some artificial "bounce" time unless that threshold is so large that the waypoints become meaningless.
If you use originalDistance - distanceCovered <= 0.0f, you will always move on to the next waypoint if it overshoots. Instead of trying to land the object in some small window, you're just making sure that the object has passed or met its waypoint.

Xna Isometric point to click movement

I'm working on an isometric game (diamond grid) and I've stumbled across a minor problem regarding a character movement.
I'm using A* to find a path between 2 points and then I want to move my character from point A to point B going through all the tiles which form the path but I can't find a way to do this , I mean a simpler and accurate method.
So far I've scrapped this piece of code but it's kinda "rusty"
public void Destination(tile destination)
{
for (int i = 0; i < 8; i++)
{
if (AdjacentTile[i] == destination)
{
characterDirection = i;
}
}
animation.changeSpriteDirection(characterDirection); //After I found which adjacent tile is the next destination I change the character direction based on it's position (1 = North , 2 = Nort Est etc) .. so the Y of the Animation_sourceRectangle it's changed//
Vector2 Position;
Position.X = current_characterTile.X - destination.X;
Position.Y = current_characterTile.Y - destination.Y;
rotation = (float)Math.Atan2(-Position.X, Position.Y);
moveVector = (Vector2.Transform(new Vector2(0, -1), Matrix.CreateRotationZ(rotation))) * characterSpeed;
movingCommand = 1; // the character is supposed to be moving..
Move(); //this function moves the sprite until the *tile.i and tile.j* of the character is the same as tile.j and tile.i of the destination
//something like this
if ( characterTile.i == destination.i && characterTile.j == destination.j)
movingCommand = 0 //stop
else
character_Position += moveVector;
}
If anyone could give me a hint on what to do or help me I'll be very grateful.
Thank You.
Possibilities:
At each tile, determine the character's speed vector and also determine how much time it will take for the character to move to next tile. When that time elapses, immediately begin moving to the next tile. (This is what I implemented below.)
At each tile, determine the character's speed vector. Then, when the character is sufficiently close to the next tile (say, the difference between their X and Y coordinates is less than 2 pixels?), snap it to the tile and begin moving to the next tile. This will causes artifacts and be in general less precise.
A solution:
Let's assume you already ran your pathfinding algorithm and found a linked list of a tiles that you must go through to arrive at target. Let's also assume those tiles cannot become blocked partway through the movement (it is simple to modify the algorithm if they can, though).
I usually do something like this to handle this problem:
Run the pathfinding algorithm, which returns a List, if a path
exists.
character.Path = theListThatAStarReturned;
character.beginMovingToTarget(character.Path[0]);
character.Path.RemoveAt(0);
The beginMovingToTarget() method will determine the velocity vector and also determine the the time needed to arrive at the tile. When the time is reached, we immediately go to the next tile, until the Path is empty. Let's call this time variable character.timeToArrival.
Update():
if (!character.Moving) return; // Or just don't execute the rest of this code.
character.position += character.speed * elapsedSeconds;
character.timeToArrival -= elapsedSeconds;
// Did the character arrive in a tile?
if (character.timeToArrival <= 0)
{
// This will ensure the character is precisely in the tile, not a few pixels veered off.
character.position = character.movingToTile.position;
if (character.Path.Count == 0)
{
character.Moving = false;
// We are at final destination.
}
else
{
character.beginMovingToTarget(character.Path[0]);
character.Path.RemoveAt(0);
}
}
And the beginMovingToTarget(targetTile) function:
this.movingToTile = targetTile;
Vector2 direction;
direction = targetTile.position - this.position;
this.timeToArrival = direction.Length() / this.speedPerSeconds;
direction.Normalize();
direction *= this.speedPerSeconds;
this.speed = direction;
// Here, you may also want to change the character's animation, if you want to, or you may do that directly in the Draw() method based on its speed vector.
Make sure the division is in floats, not integers.

Categories

Resources