Transition from one path-finding grid to another - c#

for some context, I'm creating a simple game for a university project where there are multiple seperate "islands" that the player can walk to.
The player also has a dog companion that is using an A* pathfinding algorithm that randomly selects an area within a certain range of the player, checks if they can walk there and it's not too close to where the dog already is, and returns true or not.
The issue I'm coming across is whilst the code works somewhat perfectly on the first island, when transitioning to the second island, it appears that every path that the dog tries to take gets rejected and causes a stack overflow error.
I've had a look at the debug inspector within unity to make sure that the scripts are referencing the scripts that they depend on rather than the first islands scripts, but it all appears to be correct.
I'm hoping to get some fresh eyes on this as it's only been me working on this for a few days now.
The code the error is getting created on:
public Node NodeFromWorldPoint(Vector3 worldPosition)
{
float percentX = (worldPosition.x - transform.position.x) / gridWorldSize.x + 0.5f - (nodeRadius / gridWorldSize.x);
float percentY = (worldPosition.z - transform.position.z) / gridWorldSize.y + 0.5f - (nodeRadius / gridWorldSize.y);
percentX = Mathf.Clamp01(percentX);
percentY = Mathf.Clamp01(percentY);
int x = Mathf.FloorToInt(Mathf.Min(gridSizeX * percentX, gridSizeX-1));
int y = Mathf.FloorToInt(Mathf.Min(gridSizeY * percentY, gridSizeY - 1));
return grid[x, y];
}
This is the code that generates a random location within a range and returns if it's valid or not:
public Vector3 GetRandomPointInBounds(Bounds bounds)
{
desintationReached = false;
Vector2 location;
location = new Vector2(
Random.Range(bounds.min.x, bounds.max.x),
Random.Range(bounds.min.y, bounds.max.y)
);
if (CheckIfPointInCollider(location))
{
//Debug.DrawLine(transform.position, location,Color.green,20f);
return new Vector3(location.x, location.y, 0);
}
else
{
Debug.DrawLine(transform.position, location, Color.red, 0.1f);
//Debug.Log("Invalid Location:" + location);
return GetRandomPointInBounds(bounds);
}
}
public bool CheckIfPointInCollider(Vector2 location)
{
Node targetNode = grid.NodeFromWorldPoint(new Vector3(location.x, location.y, 0));
if (targetNode.walkable && !rangeToIgnore.OverlapPoint(location))
{
return true;
}
else
{
return false;
}
}
Heres the code that transitions between the first grid object and the second:
public void Launch()
{
// Increasing Current Island Count
gm.IncreaseIsland();
Debug.Log("Transitioning to island: " + gm.GetCurrentIsland());
// Enabling & Disabling Monty, Player, Canoe Single and their depencies
canoeAIO.transform.position = canoeSpawnPoints[0].transform.GetChild(currentIsland).transform.position;
canoeAIO.SetActive(true);
canoeAIO.GetComponent<CanoePaddle>().beached = false;
canoeAIO.GetComponent<CanoePaddle>().canPaddle = true;
canoe.SetActive(false);
player.SetActive(false);
monty.SetActive(false);
layerManager.SetActive(false);
interactionsManager.SetActive(false);
pathwayCollider.SetActive(false);
spritesManager.SetActive(true);
//Setting all other pathfinding managers to inactive
for (int i = 0; i < pathfindingManagers.Length; i++)
{
pathfindingManagers[i].SetActive(false);
}
// Activating the next islands pathfinding and clearing any existing path requests
pathfindingManagers[gm.GetCurrentIsland()].SetActive(true);
PathRequestManager.ClearRequests();
monty.GetComponent<MontyStateActions>().StopAllCoroutines();
montyStateVariables.grid = pathfindingManagers[gm.GetCurrentIsland()].GetComponent<MyGrid>();
montyStateVariables.currentlyOnPath = false;
//hide monty
//hide canoe object
//show canoe all in one at the new location
cameraHandler.SwitchToCanoe();
}
(btw, Monty is the name of the dog :) )
I hope this is helpful, and if there is any more code/images you need I'll try and get them uploaded

Related

Finding the closest vertex below a certain height, from a start position

I have procedurally generated Islands with lakes, its basically a 3D mesh that has points above the water line and points below it, any vertex/point below the water level is water, everything above it is solid ground.
From any point on the mesh I want to know the closest distance to this water.
What I ended up doing was creating an Array of Vector2s, the array contains all the points on the mesh that are below the water level.
Next I wish to cycle through these elements and compare them all to find the closest one to my selected point. I am using Vector2.Distance for this because I only want the distance in the XZ components and not going up/down (Y Component).
The problem is that for most points I select this works absolutely fine, giving correct results, but sometimes it doesn't take the closest water point but instead one that is further away, even though this closer water point is confirmed to be in the array of water points that are being compared to find the closest one.
here is my code:
chunk.Vertices = new Vertice[totalVertices];
for (int i = 0, z = 0; z <= chunkSizeZ; z++)
{
for (int x = 0; x <= chunkSizeX; x++, i++)
{
Vertice vert = new Vertice();
vert.index = i;
vert.position = new Vector3(chunkStartPosition.x + x,
chunkStartPosition.y,
chunkStartPosition.z + z);
vert.centerPosition = new Vector3(vert.position.x + 0.5f,
vert.position.y,
vert.position.z + 0.5f);
vert.centerPos2 = new Vector2(vert.position.x + 0.5f,
vert.position.z + 0.5f);
chunk.Vertices[i] = vert;
}
}
Here we get all the water positions:
for (int i = 0; i < totalVertices; i++)
{
if (chunk.Vertices[i].position.y > heightCorrection + tileColliderMinimumY)
{
worldVectorsClean.Add(chunk.Vertices[i].position);
worldIndexClean.Add(chunk.Vertices[i].index);
}
else
{
worldVectorsWater.Add(chunk.Vertices[i].centerPos2);
}
}
Every single tile then calls this function on the generator itself, but only AFTER the whole map and all water points are added. Because the generator keeps track of ALL waterpoints across all chunks otherwise each chunk will only compare its own waterpoints which doesn't work because water from another chunk can be closer but wont be compared to if we don't do it this way;
public float CalculateDistanceToWater(Vector2 pos)
{
var distance = 9001f;
foreach (Vector2 waterVector in worldVectorsWater)
{
var thisDistance = Vector2.Distance(pos, waterVector);
if (thisDistance < distance)
distance = thisDistance;
}
return distance;
}
Finally when we call it from
IEnumerator FindWater()
{
yield return new WaitForSeconds(Random.Range(0.8f, 2.55f));
var pos = new Vector2(transform.position.x, transform.position.z);
distanceToWater = ChunkGenerator.instance.CalculateDistanceToWater(pos);
}
Looking forward to some help on this.

Slow update time with tiles and MouseState MonoGame

Very new developer here.
In my program I have a randomly generating world map using a simplex noise library. On top of this I am attempting to draw a tilemap of transparent 4x4 tiles that appear slightly translucent when the mouse is hovering over one.
I've got this working but it takes about 3 whole seconds for the highlighted tile to update to the mouse's current position. Is there anything I could do to solve this?
This is my code for the MouseState check in the tile class:
public override void Update(GameTime gameTime)
{
_previousMouse = _currentMouse;
_currentMouse = Mouse.GetState();
var mouseRectangle = new Rectangle(_currentMouse.X, _currentMouse.Y, 1, 1);
_isHovering = false;
if (mouseRectangle.Intersects(Rectangle))
{
_isHovering = true;
if (_currentMouse.LeftButton == ButtonState.Released && _previousMouse.LeftButton == ButtonState.Pressed)
{
Click?.Invoke(this, new EventArgs());
}
}
}
Sorry if this is formatted wrong or badly asked, first post so still getting to grips with everything :)
Invert the logic:
Instead of checking thousands of tile objects against the mouse, apply the mouse to a single object.
Assuming you have a list or array of tile objects:
Add a new object to check for mouse hover and click:
public class MouseDetect(Tile[] tiles) // replace with List<> as needed
{
int PrevHover = -1; // used to unHover
// if (Area == Screen) make the next two lines `const`, so the compiler will remove all uses...
int AreaX = 0; //Area x offset
int AreaY = 0; //Area y offset
int AreaW = 800; //Area width
int AreaH = 480; //Area height
const int Grid = 4; // assumes square
const int GridW = AreaW / Grid;
// I Will assume the `Delegate Click` in `Tile` is public
public void Update(MouseState ms, MouseState oms), //_currentMouse = ms and _previousMouse = oms;
{
int mouseIndex = (ms.X - AreaX) % Gridw + (ms.Y - AreaY) / GridW;
tiles[PrevHover].Hover = false;
PrevHover = mouseIndex;
tiles[PrevHover].Hover = true;
//Check Release
if(tiles[PrevHover].Hover && ms.LeftButton == ms.ButtonState.Released && oms.LeftButton == ButtonState.Pressed)
tiles[PrevHover].Click(tiles[PrevHover], new EventArgs());
}
}
Remove the Update from the Tile class.
Notes for anyone reading this later:
Never call Mouse.GetState(); more than once per step.
Predefined or framework names such as Rectangle should never be used as an identifier.
i.e renamed and corrected to CollRectangle
if (CollRectangle.Contains(ms.Position))

Ui button doesn't move correctly

I have a code for a crafting system that checks if the inventory has the ingredients needed to craft an item and adds a button to craft it. The problem is when I want to position my button it goes way off the canvas. I have seen some people saying that it has something to do with rect transform. I've been stuck with it for over an hour. Any help is appreciated.
I have tried
removing the setparent() function,
using anchoredPosition,
using localPosition
My code
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Crafting : MonoBehaviour
{
public List<recipe> recipes = new List<recipe>();
public GameObject base_item, parent;
List<GameObject> items = new List<GameObject>();
public int y = 75;
public int x = -45;
public Inv inv;
private void Start()
{
inv = GetComponent<Inv>();
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.Tab))
{
checkitems();
Debug.Log("y = " + y + " x = " + (x - 40));
}
}
public void checkitems()
{
for (int i = 0; i < recipes.Count; i++)
{
recipe r = recipes[i];
for (int x = 0; x < r.ingredients.Count; x++)
{
if (!inv.hasitem(r.ingredients[x])){
return;
}
}
showitem(r.result);
}
}
public void onClick(int _slot)
{
recipe r = recipes[_slot];
for (int i = 0; i < r.ingredients.Count; i++)
{
inv.removeitem(inv.getitem(r.ingredients[i]));
}
inv.additem(inv.getFirstAvailable(), r.result, r.stack);
}
public void showitem(string name)
{
GameObject obj = Instantiate(base_item);
if (items.Count != 0)
{
if (((items.Count) % 3) != 0)
{
Debug.Log("first thing");
obj.GetComponent<RectTransform>().position = new Vector2(x, y);
obj.transform.SetParent(parent.transform);
obj.SetActive(true);
items.Add(obj);
x = x + 40;
Debug.Log("x + 40");
}
else if (((items.Count + 1) % 3) == 0)
{
Debug.Log("second thing");
x = -45;
Debug.Log("x + 40");
y = y + 40;
Debug.Log(" y + 40");
obj.GetComponent<RectTransform>().position = new Vector2(x, y);
obj.transform.SetParent(parent.transform);
obj.SetActive(true);
items.Add(obj);
}
}else
{
obj.GetComponent<RectTransform>().position = new Vector2(x, y);
obj.transform.SetParent(parent.transform);
obj.SetActive(true);
items.Add(obj);
x = x + 40;
Debug.Log("x + 40");
}
}
}
Blue circle where it spawns. Red circle where I want it to be
Seems you are confusing a bunch of terms for being the issue of your problem. Firstly I want to address the red X over your scroll bar. Whenever this occurs, it means that your RectTransform of this UI object has been dragged from its positive vertices to negative or vice versa, causing it to almost invert. I would correct this but it is not the reason your objects are not childing correctly.
Generally, with UI objects, I would never use LocalPosition, just AnchoredPosition. LocalPosition is a field from Transform which I believe RectTransform inherits from. As RectTransforms have a lot of modifications to their position from pivots, anchors, and anchored positions, the LocalPosition will most likely need to recalculate data to properly move the object, whereas AnchoredPosition has already done these calculations.
I believe the issue with your current code is how you are using SetParent. There is a second parameter of SetParent which governs whether the object keeps the same position based in world space after being childed. As you are not passing in a new bool for this parameter, it is defaulting to true. As you want your objects to be childed to the parent but not keep their world space positions, you would want to pass in false.
In your case, as it looks as if you want to set objects in a grid-like pattern childed to this ScrollRect, I would attach a GridLayoutGroup to the Content of your scroll and child the new objects to this object. You can set the max columns of this grid and spacing to give the same layout you are attempting to achieve in code.
To summarize, I would remove all the hand placement you are doing in code with LocalPosition and AnchorPosition and just attach a GridLayoutGroup. To fix the current positioning of your objects relative to the parent, change all lines of obj.transform.SetParent(parent.transform); to obj.transform.SetParent(parent.transform, false);. If you want to keep changing position locally in code instead of a layout element, use SetParent first, and use AnchoredPosition instead of LocalPosition as the SetParent with false passed in will override the position you set.

Unity3D Changing texture at runtime not working on Android build

I'm working on a digital book for an Android tablet on Unity 5. I'm using this Page Curl plugin from the Asset Store.
I have 3 main planes: LeftPage, RightPage and TurningPage, and I keep an index of which page I'm looking for each one of this pages so I can manage the corresponding textures. When I click on one of the Left or Right pages, the turning page animation starts playing and when it ends, I update the index values and the corresponding textures.
It works perfect in Unity Editor and perfect with Unity Remote in the tablet, but when I run the installed apk, the turning page is always showing the textures assigned at the begining. The Left and Right pages work fine.
Printing the assigned texture name (pageFront.mainTexture.name) I get the correct texture which is equivalent to the page index value, but the turningPage keeps showing the initial (indPage = 0) texture.
All the textures are asigned on the editor on a Texture2D array.
void Update()
{
...
// If click is in one of four defined areas.
if (mouseX > 970 && mouseX < 1024 && canFlipFwd)
{
if (mouseY > 250 && mouseY < 710)
{
clickCase = 1; // Play animation 1.
changePageFwd();
}
else if (mouseY > 50 && mouseY <= 250)
{
clickCase = 2; // Play animation 2.
changePageFwd();
}
}
else
{
...
}
}
void PageFlipOver(bool pageTurned)
{
if (pageTurned)
{
// Update page index.
indRight += 2;
indPage += 2;
if (indLeft + 2 < pages.Length)
{
indLeft += 2;
leftBackground.mainTexture = pages[indLeft];
if (indLeft + 2 < pages.Length)
pageBack.mainTexture = pages[indLeft + 2];
else
pageBack.mainTexture = pages[indLeft];
}
if (!canFlipBkw)
canFlipBkw = true;
}
canFlip = true;
}
void changePageFwd()
{
if (indRight < pages.Length)
{
rightBackground.mainTexture = pages[indRight];
}
else
{
rightBackground.mainTexture = alpha;
canFlipFwd = false;
}
if (indPage < pages.Length)
pageFront.mainTexture = pages[indPage];
}
Any help is welcome, I've been stuck with this for two weaks and I can't find whats wrong.

2D collision detection not working XNA C#

I am brand new to XNA and C# (I started 2 days ago, so please forgive the sloppy programming), and I have run into a problem with a simple game I am making. I can't seem to find the solution in any of the online tutorials. I am trying to utilize rectangle collision detection but I cannot get it to work properly. The character falls completely through the floor and it seems there is no collision ever registering in any place. I cannot find my logic error here. Below I posted a bit of my code that pertains to the collision detection. I can post more if necessary. Thank you for the help in advance!
Level.cs
//The method below is called in the initial LoadContent method in Game1.cs. 'LoadBlocks()' is supposed to read in a text file and setup the level's "blocks" (the floor or platforms in the game).
public void LoadBlocks(int level){
if (level == 0)
{
fileName = "filepath";
}
System.IO.StreamReader file = new System.IO.StreamReader(fileName);
while ((line = file.ReadLine()) != null)
{
for (int i = 0; i < 35; i++)
{
rectBlock = new Rectangle((int)positionBlock.X, (int)positionBlock.Y, width / 30, height / 30);
if (line.Substring(i, 1).Equals(","))
{
positionBlock.X += (width / 100) * 24;
}
if (line.Substring(i, 1).Equals("#"))
{ //block -> (bool isPassable, Texture2D texture, Rectangle rectangle, Vector2 position)
block = new Block(false, textureBlock, rectBlock, positionBlock);
blocks.Add(block);
positionBlock.X += (width / 100) * 24;
}
}
positionBlock.Y += (height / 100) * 8;
positionBlock.X = 0;
}
}
//This method below updates the character 'bob' position and velocity.
public void UpdatePlayer()
{
bob.position += bob.velocity;
bob.rectangle = new Rectangle((int)bob.position.X, (int)bob.position.Y, width / 30, height / 30);
float i = 1;
bob.velocity.Y += 0.15f * i;
foreach (Block block in blocks)
{
if (bob.isOnTopOf(bob.rectangle, block.rectangle))
{
bob.velocity.Y = 0f;
bob.hasJumped = false;
}
}
}
Character.cs
//Here is my whole Character class for 'bob'
public class Character
{
int height = GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Height;
int width = GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Width;
int health;
String name;
bool gender;
Texture2D texture;
public Vector2 position;
public Vector2 velocity;
public bool hasJumped;
public Rectangle rectangle;
public Character(int newHealth, String newName, bool newGender, Texture2D newTexture, Vector2 newPosition)
{
health = newHealth;
gender = newGender;
name = newName;
texture = newTexture;
position = newPosition;
hasJumped = true;
rectangle = new Rectangle(0,0, width/30,height/30);
velocity = Vector2.Zero;
}
public bool isOnTopOf(Rectangle r1, Rectangle r2)
{
const int penetrationMargin = 5;
return (r1.Bottom >= r2.Top - penetrationMargin &&
r1.Bottom <= r2.Top &&
r1.Right >= r2.Left + 5 &&
r1.Left <= r2.Right - 5);
}
public void Draw(SpriteBatch spriteBatch)
{
spriteBatch.Draw(texture, rectangle,null, Color.White,0,position,SpriteEffects.None,0);
}
}
Sorry if this is too much posted code or is confusing, but any help is greatly appreciated! Thanks!
Your test condition for intersection appears to be incorrect, at least. There might be other problems with the code (there are several just giving a quick look at it), but let's focus on this:
r1.Bottom >= r2.Top - penetrationMargin &&
r1.Bottom <= r2.Top &&
r1.Right >= r2.Left + 5 &&
r1.Left <= r2.Right - 5;
First, don't code what exists in the library for you. Xna Rectangles have an Intersects method, so scrap that code and use it instead.
We'll look at the condition anyway:
r1's Bottom is below r2's Top minus a margin AND
r1's Bottom is above r2's Top AND (...)
This is already impossible. r1.Bottom cannot be both below and above r2.Top.
Further, even if all the comparisons were correct, you are using AND everywhere (&&), which means that the condition is only true if all 4 of them are true. Basically, you are not testing for intersection, you are testing for containement, i.e. you are testing that r1 is entirely within r2. An intersection test would join the conditions using OR (||) not AND (&&).
But again, although you might want to code it yourself for educative purposes, the best solution from an engineering perspective is to use what the library provides, here Rectangle.Intersects.
I figured it out, but I'm not sure why this actually works. Something was wrong with my draw method. I originally had:
spriteBatch.Draw(texture, rectangle, null, Color.White, 0, position, SpriteEffects.None, 0);
as my method but then I changed this to
spriteBatch.Draw(texture, rectangle, Color.White);
and now everything works fine.

Categories