Unity-How to Keep the Player out of the Walls - unity3d

So I put together a procedural generated map, and when I click it, the map randomly changes. What it does is it loops through the plane and randomly generates a mesh depending on the square's placement compared to randomfillpercent. If the map coordinate is marked 1, it's a wall, and if it's 0, its open space. I'm having an issue where if I click the map, the player sphere ends up inside the randomly generated wall. I want to make it so if the player's position is equal to a map coordinate that's a wall, then move it down the map until it reaches open space. Unfortunately, I keep getting null reference errors. I anyone could give me some ideas, I would appreciate it. Here's my variables and my RandomFillMap function. I'm not showing the whole code. If there's something you need to see, let me know. Thank you.
public class MapGeneratorCave : MonoBehaviour {
public int width;
public int height;
public string seed;
public bool useRandomSeed;
public GameObject player;
int[,] playerPosition;
[Range(0, 100)]
public int randomFillPercent;
int[,] map;
void Start()
{
GenerateMap();
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
GenerateMap();
}
}
void GenerateMap()
{
map = new int[width, height];
RandomFillMap();
for (int i = 0; i < 5; i++)
{
SmoothMap();
}
ProcessMap();
int borderSize = 1;
int[,] borderedMap = new int[width + borderSize * 2, height + borderSize * 2];
for (int x = 0; x < borderedMap.GetLength(0); x++)
{
for (int y = 0; y < borderedMap.GetLength(1); y++)
{
if (x >= borderSize && x < width + borderSize && y >= borderSize && y < height + borderSize)
{
borderedMap[x, y] = map[x - borderSize, y - borderSize];
}
else
{
borderedMap[x, y] = 1;
}
}
}
MeshGenerator meshGen = GetComponent<MeshGenerator>();
meshGen.GenerateMesh(borderedMap, 1);
}
void RandomFillMap()
{
int playerX = (int)player.transform.position.x;
int playerY = (int)player.transform.position.y;
if (useRandomSeed)
{
seed = Time.time.ToString();
}
System.Random pseudoRandom = new System.Random(seed.GetHashCode());
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
if (x == 0 || x == width - 1 || y == 0 || y == height - 1)
{
map[x, y] = 1;
}
else
{
map[x, y] = (pseudoRandom.Next(0, 100) < randomFillPercent) ? 1 : 0;
}
if (playerPosition[playerX, playerY] == map[x, y] && map[x, y] == 1)
{
playerPosition[playerX, playerY] = map[x, y - 1];
}
}
}
}

You're probably getting null references because playerPosition[playerX, playerY] doesn't exist.
What you should be using instead of a multi dimensional array (int[,]) is a Vector2
Then you would do something like this:
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
if (x == 0 || x == width - 1 || y == 0 || y == height - 1)
{
map[x, y] = 1;
}
else
{
map[x, y] = (pseudoRandom.Next(0, 100) < randomFillPercent) ? 1 : 0;
}
}
}
while(map[playerPosition.x, playerPosition.y] == 1){
playerPosition.y --;
// make sure you aren't out of bounds and such
}

Related

How to generate a honeycomb field in Unity?

I need to generate such a field:
Photo
But I don't know how to do it. What happened to me:
My result
My code:
[ContextMenu("Generate grid")]
public void GenerateGrid()
{
for(int x = 0; x < _gridSize.x; x++)
{
for (int z = 0; z < _gridSize.z; z++)
{
var meshSize = _cell.GetComponent<MeshRenderer>().bounds.size;
var position = new Vector3(x * (meshSize.x + _offset), 0, z * (meshSize.z + _offset));
var cell = Instantiate(_cell, position, Quaternion.Euler(_rotationOffset), _parent.transform);
cell.GridActions = GridActions;
cell.Position = new Vector2(x, z);
cell.name = $"Cell: x:{x}, z:{z}";
GridActions.AllCell.Add(cell);
}
}
}
Simply for every odd z value, move the cell up/down by half a cell size, and move them inward toward the previous cell half a cell size. I didnt test it, but here is the code that might do that, not sure tho, again I didnt test this.
[ContextMenu("Generate grid")]
public void GenerateGrid()
{
for(int x = 0; x < _gridSize.x; x++)
{
for (int z = 0; z < _gridSize.z; z++)
{
int xResize = 0;
int zResize = 0;
if (z % 2 == 1) {
xResize = meshSize.x / 2;
zResize = meshSize.z / 2;
}
var meshSize = _cell.GetComponent<MeshRenderer>().bounds.size;
var position = new Vector3(x * (meshSize.x + _offset - xResize), 0, z * (meshSize.z + _offset - zResize));
var cell = Instantiate(_cell, position, Quaternion.Euler(_rotationOffset), _parent.transform);
cell.GridActions = GridActions;
cell.Position = new Vector2(x, z);
cell.name = $"Cell: x:{x}, z:{z}";
GridActions.AllCell.Add(cell);
}
}
}

How to generate non-repeating Random Numbers in Unity

I am trying to create a simple Bingo game and want to make sure the numbers are not repeating on the bingo card. I have a random number generator, but for some reason the code I'm using doesn't work as the same numbers will constantly repeat. Could somebody please take a look at my code below and either tell me what I need to fix or fix the code for me?
public Grid(int width, int height, float cellSize)
{
this.width = width;
this.height = height;
this.cellSize = cellSize;
gridArray = new int[width, height];
debugTextArray = new TextMesh[width, height];
for (int x = 0; x < gridArray.GetLength(0); x++)
{
for (int y = 0; y < gridArray.GetLength(1); y++)
{
debugTextArray[x, y] = UtilsClass.CreateWorldText(gridArray[x, y].ToString(), null, GetWorldPosition(x, y) + new Vector3(cellSize, cellSize) * .5f, 20, Color.white, TextAnchor.MiddleCenter);
Debug.DrawLine(GetWorldPosition(x, y), GetWorldPosition(x, y + 1), Color.white, 100f);
Debug.DrawLine(GetWorldPosition(x, y), GetWorldPosition(x + 1, y), Color.white, 100f);
}
}
Debug.DrawLine(GetWorldPosition(0, height), GetWorldPosition(width, height), Color.white, 100f);
Debug.DrawLine(GetWorldPosition(width, 0), GetWorldPosition(width, height), Color.white, 100f);
for (int x = 0; x <= 4; x++)
{
RandomValue(0, x);
RandomValue(1, x);
RandomValue(2, x);
RandomValue(3, x);
RandomValue(4, x);
}
}
private Vector3 GetWorldPosition(int x, int y)
{
return new Vector3(x, y) * cellSize;
}
public void RandomValue(int x, int y)
{
if (x >= 0 && y >= 0 && x < width && y < height)
{
list = new List<int>(new int[Lenght]);
for (int j = 0; j < 25; j++)
{
Rand = UnityEngine.Random.Range(1, 50);
while (list.Contains(Rand))
{
Rand = UnityEngine.Random.Range(1, 50);
}
list[j] = Rand;
gridArray[x, y] = list[j];
}
debugTextArray[x, y].text = gridArray[x, y].ToString();
debugTextArray[2, 2].text = "Free";
}
}
Basically your concept in function RandomValue() is correct, but problem is it only check in same column, so you have to bring the concept of RandomValue() to Grid() level. You need a List contain all approved value, then check Contains() at Grid().
But in fact you can do it in all one go.
Make sure your width*height not larger than maxValue.
Dictionary<Vector2Int, int> CreateBingoGrid(int width, int height, int maxValue)
{
var grid = new Dictionary<Vector2Int, int>();
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
var num = Random.Range(1, maxValue);
while (grid.ContainsValue(num))
{
num = Random.Range(1, maxValue);
}
grid.Add(new Vector2Int(x, y), num);
}
}
return grid;
}
As mentioned in the comment on your question, it's probably the easiest to just shuffle the numbers in the range [1,50] and then take the first 25 or however many you want.
The reason your code isn't working properly and you see a lot of repeats is because you're calling the RandomValue() function multiple separate times and the list variable you're comparing against if a value is already on the chart is inside of that function. Meaning that it will only ever check the values it has generated in that call, in this case meaning only for one row.
Also, if you make a list that you know will always be the same size, you should use an array instead. Lists are for when you want the size to be adjustable.
Solution 1:
A very simple way to generate an array with the numbers 1-50 would be to do this:
//Initialize Array
int[] numbers = new int[50];
for (int i = 1; i <= numbers.Length; i++)
{
numbers[i] = i;
}
//Shuffle Array
for (int i = 0; i < numbers.Length; i++ )
{
int tmp = numbers[i];
int r = Random.Range(i, numbers.Length);
numbers[i] = numbers[r];
numbers[r] = tmp;
}
//Get first 'n' numbers
int[] result = Array.Copy(numbers, 0, result, 0, n);
return result;
I'm not sure if it's the most efficient way, but it would work.
Solution 2:
To change your code to check against the entire list, I would change this section:
for (int x = 0; x <= 4; x++)
{
RandomValue(0, x);
RandomValue(1, x);
RandomValue(2, x);
RandomValue(3, x);
RandomValue(4, x);
}
To something like this:
List<int> values = new List<int>();
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
int r = RandomValue(1, 50);
while (values.Contains(r))
{
r = RandomValue(1, 50);
}
values[y * width + x].Add(r);
gridArray[x, y] = r;
}
}
int RandomValue(int min, int max) {
return UnityEngine.Random.Range(min, max);
}
Hope this helps!

Unity perlin noise having repeating patterns

I made a Noise class using the Perlin Noise from Unity like this:
public static float[,] GetNoise(Vector2Int initialOffset, float scale, float persistance, float lacunarity, int octaves)
{
float[,] noiseMap = new float[Chunk.width, Chunk.height];
float maxHeight = 0;
float minHeight = 0;
for (int y = 0; y < Chunk.height; y++)
{
for (int x = 0; x < Chunk.width; x++)
{
float amplitude = 1;
float frequency = 1;
float noiseHeight = 0;
for (int oc = 0; oc < octaves; oc++)
{
float coordX = (x + initialOffset.x) / scale * frequency;
float coordY = (y + initialOffset.y) / scale * frequency;
float perlin = Mathf.PerlinNoise(coordX, coordY) * 2 - 1;
noiseHeight += perlin * amplitude;
amplitude *= persistance;
frequency *= lacunarity;
}
if (noiseHeight < minHeight)
{
minHeight = noiseHeight;
}
if (noiseHeight > maxHeight)
{
maxHeight = noiseHeight;
}
noiseMap[x, y] = noiseHeight;
}
}
for (int y = 0; y < Chunk.height; y++)
{
for (int x = 0; x < Chunk.width; x++)
{
noiseMap[x, y] = Mathf.InverseLerp(minHeight, maxHeight, noiseMap[x, y]);
}
}
return noiseMap;
}
However this code is giving me repeating patterns like this:
What am I doing wrong? Or there is no way to get rid of the patterns?
I got it working, not very well, but working. The way I did was I generate the height map for every tile in the chunk, then I did some random placing of tiles, while having in account the height map. Something like this:
if (heightMap[x, y] < 0.3 && Random.value < 0.5)
// Add tile
This way I got this result:
EDIT:
Doing some more research about Perlin Noise I found out that it just doesn't like negative coords for some reason, so I did this way, hope this helps someone!
so .. fixed the negative coords like this:
//account for negatives (ex. -1 % 256 = -1, needs to loop around to 255)
if (noiseOffset.x < 0)
noiseOffset = new Vector2(noiseOffset.x + noiseRange.x, noiseOffset.y);
if (noiseOffset.y < 0)
noiseOffset = new Vector2(noiseOffset.x, noiseOffset.y + noiseRange.y);

To create a bounding box in a texture in Unity

I want to make a bounding box in texture.
This texture is the result of image Segmentation
I make Segmentation results into textures every time and I try to do a bounding box for this process
I have pixel-specific values for texture, but this changes randomly every time.
so, I tried to find pixel values with bfs algorithm.
public Queue<Node> SegNode = new Queue<Node>();
private bool[,] visit = new bool[256, 256];
private int[] dx = new int[4] { 0, 1, -1, 0 };
private int[] dy = new int[4] { 1, 0, 0, -1 };
public struct Node
{
public int x, y;
public float color;
public Node(int x, int y, float color)
{
this.x = x;
this.y = y;
this.color = color;
}
}
void bfs(int r, int c, float color, float[,,,] pixel)
{
Queue<Node> q = new Queue<Node>();
q.Enqueue(new Node(r, c, color));
while (q.Count > 0)
{
Node curNode = q.Dequeue();
SegNode.Enqueue(curNode);
for (int i = 0; i < 4; i++)
{
int tr = curNode.x + dx[i];
int tc = curNode.y + dy[i];
if (tr >= 0 && tr < segmentationImageSize && tc >= 0 && tc < segmentationImageSize)
{
if (!visit[tr, tc] && pixel[0, tc, tr, 0] == color)
{
visit[tr, tc] = true;
q.Enqueue(new Node(tr, tc, color));
}
}
}
}
And I thought about how to find the top and bottom.
But this seems to be too slow.
How can I get a bounding box easily?
I am using the result values for the segmentation to create a texture
Texture2D SegResTexture = new Texture2D(widht, height, );
for (int y = 0; y < SegResTexture.height; y++)
{
for (int x = 0; x < SegResTexture.width; x++)
{
SegResTexture.SetPixel(x, y, pixel[0, y, x, 0] < 0.1 ? maskTransparent : maskColor);
}
}
SegResTexture.Apply();
SegmentationRawIamge.GetComponent<RawImage>().texture = SegResTexture;
What I want to do is similar to the picture below.
Can you tell me how to make it or what sites to refer to?

How to get the hash key?

When I solve this question(149. Max Points on a Line) on leetcode, it have a bug when met this case:
Input [[0,0],[94911151,94911150],[94911152,94911151]]
Output 3
Expected 2
This is my code:
/**
* Definition for a point.
* struct Point {
* int x;
* int y;
* Point() : x(0), y(0) {}
* Point(int a, int b) : x(a), y(b) {}
* };
*/
class Solution {
public:
int maxPoints(vector<Point>& points) {
int size = points.size();
int ans = 0;
if (size == 0) return 0;
unordered_map<double, int> mp;
double k;
for (int i = 0; i < size; ++i) {
int num = 0;
for (int j = i + 1; j < size; ++j) {
if (points[i].x == points[j].x && points[i].y == points[j].y) {
num++;
continue;
}
// my question in below code.
// how can I get the hash key according to slope
if (points[j].x - points[i].x != 0)
k = (double)(points[j].y - points[i].y) / (double)(points[j].x - points[i].x); // calculate the slope.
else k = INT_MAX;
mp[k]++;
}
if (mp[k] == 0) mp[k] = 1, num--;
for (auto it = mp.begin(); it != mp.end(); ++it) {
if (it->second > ans) {
ans = it->second;
ans += num;
}
}
mp.clear();
}
return ans+1;
}
};
In above test case, when it calculate the slope with [0,0] and [94911151,94911150] it comeback k = 1. So I want to know how to get the right hash key to solve this problem?