I am having trouble with Unity Physics.CheckSphere. I have added a mask "Wall" and assigned it to the object.
In my custom editor window I can select this mask and set values as a grid to check for this object. However, when I click the button I don't get any collisions. But when I change the object's mask to something like "TransparentFX" I get the results I expected.
What am I missing here?
Here is the code for the custom editor window:
private void OnGUI()
{
GUILayout.Label("Generate A*", EditorStyles.boldLabel);
showGridTools = EditorGUILayout.BeginFoldoutHeaderGroup(showGridTools, "Grid Tools");
if(showGridTools)
{
this.GridDimensions = EditorGUILayout.Vector3IntField("Grid Dimensions", GridDimensions);
this.ObstacleMask = EditorGUILayout.LayerField("Obstacle Mask", this.ObstacleMask);
if (GUILayout.Button("Generate Grid"))
CreateGrid();
}
EditorGUILayout.EndFoldoutHeaderGroup();
}
And for checking the collisions:
private void CreateGrid()
{
for (int x = 0; x < this.GridDimensions.x; x++)
{
for (int z = 0; z < this.GridDimensions.z; z++)
{
for (int y = 0; y < this.GridDimensions.y; y++)
{
Vector3Int position = new Vector3Int(x, y, z);
bool obstacle = Physics.CheckSphere(position, 0.3f, this.ObstacleMask);
if(obstacle)
Debug.Log($"Position: {position}, Obstacle: {obstacle}");
}
}
}
}
----------- EDIT -----------
I just did this. Might be pretty slow, I don't know, but at least it
works for now.
private void CreateGrid()
{
this.Grid = new Node[this.GridDimensions.x, this.GridDimensions.y, this.GridDimensions.z];
for (int x = 0; x < this.GridDimensions.x; x++)
{
for (int z = 0; z < this.GridDimensions.z; z++)
{
for (int y = 0; y < this.GridDimensions.y; y++)
{
Vector3Int position = new Vector3Int(x, y, z);
var collisions = Physics.OverlapSphere(position, 0.3f);
bool obstacle = collisions.Where(c => c.gameObject.layer == this.ObstacleMask).Count() > 0;
if (obstacle)
Debug.Log($"Position: {position}, Obstacle: {obstacle}");
}
}
}
}
so, I'm making a terraria-like game in unity 2019 on windws 10 using c# which has procedurally generated tilemaps and i have this script attached to a grid:
using UnityEngine;
using AccidentalNoise;
using System.Collections.Generic;
using UnityEngine.Tilemaps;
using System;
public class CompileTerrain : MonoBehaviour
{
public TileBase dirtTile;
public TileBase grassTile;
public TileBase stoneTile;
public List<GameObject> fractalLayers = new List<GameObject>();
public Tilemap grid;
public int width;
public int height;
public float seed;
public int caveSmoothness = 2;
void Start()
{
grid.ClearAllTiles();
int touchCount = 0;
Vector3Int newPos;
double nx, ny;
ModuleBase combinedTerrain = CavesAndMountains((uint)seed);
List<Vector3Int> terrainCoords = new List<Vector3Int>();
SMappingRanges ranges = new SMappingRanges();
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
nx = (ranges.mapx0 + ((double)x / (double)width) * (ranges.mapx1 - ranges.mapx0)) * 3;
ny = (ranges.mapy0 + ((double)y / (double)height) * (ranges.mapy1 - ranges.mapy0)) * 3;
if (combinedTerrain.Get(nx, ny) > 0f)
{
terrainCoords.Add(new Vector3Int(x, height - y, 0));
}
}
}
List<Tuple<int, int>> neighbors = new List<Tuple<int, int>>() {Tuple.Create(1, 1), Tuple.Create(-1, -1),
Tuple.Create(0, 1), Tuple.Create(1, 0),
Tuple.Create(0, -1), Tuple.Create(-1, 0),
Tuple.Create(-1, 1), Tuple.Create(1, -1)};
for (int index = 0; index < terrainCoords.Count; index++)
{
if (index == terrainCoords.Count)
{
break;
}
touchCount = 0;
for (int posAdd = 0; posAdd < neighbors.Count; posAdd++)
{
newPos = new Vector3Int(terrainCoords[index].x + neighbors[posAdd].Item1, terrainCoords[index].y + neighbors[posAdd].Item2, 0);
touchCount += terrainCoords.Contains(newPos) ? 1 : 0;
}
if (touchCount < 2)
{
terrainCoords.Remove(terrainCoords[index]);
}
}
for (int j = 0; j < caveSmoothness; j++)
{
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
if (!terrainCoords.Contains(new Vector3Int(x, y, 0)))
{
touchCount = 0;
for (int posAdd = 0; posAdd < neighbors.Count; posAdd++)
{
newPos = new Vector3Int(x + neighbors[posAdd].Item1, y + neighbors[posAdd].Item2, 0);
touchCount += terrainCoords.Contains(newPos) ? 1 : -1;
}
if (touchCount > 1)
{
terrainCoords.Add(new Vector3Int(x, y, 0));
}
}
}
}
}
foreach (Vector3Int blck in terrainCoords)
{
grid.SetTile(blck, stoneTile);
}
terrainCoords.Sort((x, y) => x.x == y.x ? x.y.CompareTo(y.y) : x.x.CompareTo(y.x));
terrainCoords.Reverse();
TileBase selectedTile;
int depth = 0;
int lastx = 0;
int lasty = terrainCoords[0].y + 1;
foreach (Vector3Int blck in terrainCoords)
{
depth = blck.x != lastx ? 0 : depth;
lasty = blck.x != lastx ? blck.y + 1 : lasty;
selectedTile = depth < 4 ? grassTile : stoneTile;
selectedTile = 3 < depth && depth < 30 ? dirtTile : selectedTile;
grid.SetTile(blck, selectedTile);
lastx = blck.x;
depth += lasty - blck.y;
lasty = blck.y;
}
int layerNum = 1;
List<Vector3Int> posList = new List<Vector3Int>();
foreach (GameObject layer in fractalLayers)
{
GetPerlinLayer component = layer.GetComponent<GetPerlinLayer>();
for (int k = 0; k < component.populateCount; k++)
{
layerNum++;
foreach (Vector3Int pos in component.GetFractalCoords(width, height, (uint)(seed * layerNum)))
if (grid.GetTile(pos) != null && grid.GetTile(pos) != grassTile)
{
grid.SetTile(pos, component.defaultTile);
}
}
}
}
public static ModuleBase CavesAndMountains(uint seed)
{
AccidentalNoise.Gradient ground_gradient = new AccidentalNoise.Gradient(0, 0, 0, 1);
// lowlands
Fractal lowland_shape_fractal = new Fractal(FractalType.BILLOW, BasisTypes.GRADIENT, InterpTypes.QUINTIC, 2, 0.25, seed);
AutoCorrect lowland_autocorrect = new AutoCorrect(lowland_shape_fractal, 0, 1);
ScaleOffset lowland_scale = new ScaleOffset(0.125, -0.45, lowland_autocorrect);
ScaleDomain lowland_y_scale = new ScaleDomain(lowland_scale, null, 0);
TranslatedDomain lowland_terrain = new TranslatedDomain(ground_gradient, null, lowland_y_scale);
// highlands
Fractal highland_shape_fractal = new Fractal(FractalType.FBM, BasisTypes.GRADIENT, InterpTypes.QUINTIC, 4, 2, seed);
AutoCorrect highland_autocorrect = new AutoCorrect(highland_shape_fractal, -1, 1);
ScaleOffset highland_scale = new ScaleOffset(0.25, 0, highland_autocorrect);
ScaleDomain highland_y_scale = new ScaleDomain(highland_scale, null, 0);
TranslatedDomain highland_terrain = new TranslatedDomain(ground_gradient, null, highland_y_scale);
// mountains
Fractal mountain_shape_fractal = new Fractal(FractalType.RIDGEDMULTI, BasisTypes.GRADIENT, InterpTypes.QUINTIC, 8, 1, seed);
AutoCorrect mountain_autocorrect = new AutoCorrect(mountain_shape_fractal, -1, 1);
ScaleOffset mountain_scale = new ScaleOffset(0.3, 0.15, mountain_autocorrect);
ScaleDomain mountain_y_scale = new ScaleDomain(mountain_scale, null, 0.15);
TranslatedDomain mountain_terrain = new TranslatedDomain(ground_gradient, null, mountain_y_scale);
// terrain
Fractal terrain_type_fractal = new Fractal(FractalType.FBM, BasisTypes.GRADIENT, InterpTypes.QUINTIC, 3, 0.125, seed);
AutoCorrect terrain_autocorrect = new AutoCorrect(terrain_type_fractal, 0, 1);
ScaleDomain terrain_type_y_scale = new ScaleDomain(terrain_autocorrect, null, 0);
AccidentalNoise.Cache terrain_type_cache = new AccidentalNoise.Cache(terrain_type_y_scale);
Select highland_mountain_select = new Select(terrain_type_cache, highland_terrain, mountain_terrain, 0.55, 0.2);
Select highland_lowland_select = new Select(terrain_type_cache, lowland_terrain, highland_mountain_select, 0.25, 0.15);
AccidentalNoise.Cache highland_lowland_select_cache = new AccidentalNoise.Cache(highland_lowland_select);
Select ground_select = new Select(highland_lowland_select_cache, 0, 1, 0.5, null);
// caves
Fractal cave_shape = new Fractal(FractalType.RIDGEDMULTI, BasisTypes.GRADIENT, InterpTypes.QUINTIC, 1, 4, seed);
Bias cave_attenuate_bias = new Bias(highland_lowland_select_cache, 0.65);
Combiner cave_shape_attenuate = new Combiner(CombinerTypes.MULT, cave_shape, cave_attenuate_bias);
Fractal cave_perturb_fractal = new Fractal(FractalType.FBM, BasisTypes.GRADIENT, InterpTypes.QUINTIC, 6, 3, seed);
ScaleOffset cave_perturb_scale = new ScaleOffset(0.5, 0, cave_perturb_fractal);
TranslatedDomain cave_perturb = new TranslatedDomain(cave_shape_attenuate, cave_perturb_scale, null);
Select cave_select = new Select(cave_perturb, 1, 0, 0.75, 0);
return new Combiner(CombinerTypes.MULT, cave_select, ground_select) as ModuleBase;
}
}
which i have so graciously borrowed and modified from the fine folks at accidental noise, and i made an empty gameobject which i attached this script to:
using UnityEngine;
using AccidentalNoise;
using System.Collections.Generic;
using UnityEngine.Tilemaps;
public class GetPerlinLayer : MonoBehaviour
{
public TileBase defaultTile;
public float threshold = 0.5f;
public int populateCount = 5;
public List<Vector3Int> GetFractalCoords(int width, int height, uint seed)
{
double nx, ny;
ModuleBase combinedTerrain = new Fractal(FractalType.FBM, BasisTypes.GRADIENT, InterpTypes.QUINTIC, 6, 2, seed);
List<Vector3Int> fractalCoords = new List<Vector3Int>();
SMappingRanges ranges = new SMappingRanges();
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
nx = (ranges.mapx0 + ((double)x / (double)width) * (ranges.mapx1 - ranges.mapx0)) * 3;
ny = (ranges.mapy0 + ((double)y / (double)height) * (ranges.mapy1 - ranges.mapy0)) * 3;
if (combinedTerrain.Get(nx, ny) > threshold)
{
fractalCoords.Add(new Vector3Int(x, height - y, 0));
}
}
}
return fractalCoords;
}
}
and i attached different colored square sprites for each of those gameobjects, and saved them as a prefab. Once i had that prefab, i attached that to the fractalLayers list in my previous script to generate ores. And although it runs fine on a lower scale, I cant run it on a larger scale. And since there's no cure-all for making code run faster (aside from refactoring, which i don't know how to do), and i probably could've made parts of my code more efficient since I'm a novice, i would really like some insight from the eyes of a professional on how to make my code run better. I know i didn't explain everything about my project but its really just a bare-bones project those are the only scripts and unique parts about it, you can just infer what i did and fill in the blanks. Any help is appreciated. And if you could give me the extra push along with some information on the subject, I would love to have some videos recommended along with your insight to guide me along this process, since i am more of a visual learner. Thank you! =)
(For reference, it took me about 4 minutes to build this, with the settings shown here.)
So apparently my word generation method was fine, just the loop I was using to smoothen the terrain took too long.
So I am trying to create my own version of Sebastian Lauge's marching cubes coding adventures (https://www.youtube.com/watch?v=M3iI2l0ltbE) and I have a script that generates perlin noise into terrain on the Y-axis ant Im trying to make it so it creates perlin noise for the x, y, and z axis and compile vertices from that perlin noise into a mesh. Rightnow I'm in the process of adding the code to decides which points should be put into the mesh The problem is that I have to change the variable y in lines 49 - 56 to the variable ypn (declared on line 45) and leave the y variables out of that area alone the problem is that if i change one of those y variables (don't know which one) it will make the mesh flat and that's not what I want then it gets worse. If you try to revert the variable you changed back to y it will not change a thing. It will always make the mesh flat unless you make a new script. My code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MapGen : MonoBehaviour
{
Mesh mesh;
Material material;
Vector3[] vertices;
int[] triangles;
Vector2[] uvs;
Vector3[] interest;
public int xSize = 20;
public int zSize = 20;
public int ySize = 20;
[Range(-1, 1)]
public float Surface = 0;
public float Weight = 1;
float minHeight = 0;
float maxHeight = 0;
// Start is called before the first frame update
void Start()
{
mesh = new Mesh();
GetComponent<MeshFilter>().mesh = mesh;
CreateShape();
UpdateMesh();
}
void CreateShape()
{
vertices = new Vector3[(xSize + 1) * (zSize + 1)];
Texture2D texture = new Texture2D(xSize, zSize, TextureFormat.ARGB32, false);
for (int y = 0; y <= ySize; y++)
{
for (int i = 0, z = 0; z <= zSize; z++)
{
for (int x = 0; x <= xSize; x++)
{
float ypn = (Mathf.PerlinNoise(x * .3f, z * .3f) * 2f) * Weight;
vertices[i] = new Vector3(x, ypn, z);
if (y <= minHeight)
minHeight = y;
else
{
if (y >= maxHeight)
maxHeight = y;
}
var result = Mathf.Lerp(minHeight, maxHeight, Mathf.InverseLerp(0, 1, y));
texture.SetPixel(x, z, new Color(y, x, 1));
i++;
}
}
}
texture.Apply();
GetComponent<Renderer>().material.mainTexture = texture;
triangles = new int[xSize * zSize * 6];
int vert = 0;
int tris = 0;
for (int z = 0; z < zSize; z++)
{
for (int x = 0; x < xSize; x++)
{
triangles[tris + 0] = vert + 0;
triangles[tris + 1] = vert + xSize + 1;
triangles[tris + 2] = vert + 1;
triangles[tris + 3] = vert + 1;
triangles[tris + 4] = vert + xSize + 1;
triangles[tris + 5] = vert + xSize + 2;
vert++;
tris += 6;
}
vert++;
}
uvs = new Vector2[vertices.Length];
for (int i = 0, z = 0; z <= zSize; z++)
{
for (int x = 0; x <= xSize; x++)
{
uvs[i] = new Vector2((float)x / xSize, (float)z / zSize);
i++;
}
}
}
void UpdateMesh()
{
mesh.Clear();
mesh.vertices = vertices;
mesh.triangles = triangles;
mesh.uv = uvs;
mesh.RecalculateNormals();
MeshCollider meshc = gameObject.AddComponent(typeof(MeshCollider)) as MeshCollider;
meshc.sharedMesh = mesh;
}
}
Note: using unity 2019.2.15
Script is attached to empty Game object with a mesh renderer and a mesh filter on default unity values
I'm coding an runtime terrain editor for unity and got stuck.
At first I wanted just to paint with textrue the terrain. I found this code and it worked fine:
SCRIPT: TerrainPainter
void Paint(Vector3 point)
{
mapX = (int)(((point.x - terrainPosition.x) / terrainData.size.x) * heightmapWidth);
mapY = (int)(((point.z - terrainPosition.z) / terrainData.size.z) * heigtmapHeight);
splatmapData[mapY, mapX, 0] = element[0, 0, 0] = 0;
splatmapData[mapY, mapX, 1] = element[0, 0, 1] = 1;
terrain.terrainData.SetAlphamaps(mapX, mapY, element);
}
But now I want to paint with different sizes/thickness. I have another script, named Terrainmodifier, which I use to raise and lower the terrain. There I have this lines for raising:
SCRIPT: Terrainmodifier
public void RaiseTerrain(Terrain terrain, Vector3 location, float effectIncrement)
{
int offset = areaOfEffectSize / 2;
//--1--
Vector3 tempCoord = (location - terrain.GetPosition());
Vector3 coord;
coord = new Vector3(
(tempCoord.x / GetTerrainSize().x),
(tempCoord.y / GetTerrainSize().y),
(tempCoord.z / GetTerrainSize().z)
);
Vector3 locationInTerrain = new Vector3(coord.x * terrainHeightMapWidth, 0, coord.z * terrainHeightMapHeight);
// End --1--
// --2--
int terX = (int)locationInTerrain.x - offset;
int terZ = (int)locationInTerrain.z - offset;
// End --2--
// --3--
float[,] heights = targetTerrainData.GetHeights(terX, terZ, areaOfEffectSize, areaOfEffectSize);
for (int xx = 0; xx < areaOfEffectSize; xx++)
{
for (int yy = 0; yy < areaOfEffectSize; yy++)
{
heights[xx, yy] += (effectIncrement * Time.smoothDeltaTime);
}
}
targetTerrainData.SetHeights(terX, terZ, heights);
}
So I thought I could use this as an aid and transfer it. So I took GetAlphamaps() instead of GetHeights() and added the variable areaOfEffectSize.
SCRIPT: TerrainPainter
void Paint(Vector3 point)
{
// --1--
mapX = (int)(((point.x - terrainPosition.x) / terrainData.size.x) * heightmapWidth);
mapY = (int)(((point.z - terrainPosition.z) / terrainData.size.z) * heigtmapHeight);
// End --1--
// --2--
int terX = (int)mapX - (areaOfEffectSize / 2);
int terY = (int)mapY - (areaOfEffectSize / 2);
// End --2--
// --3--
splatmapData = terrainData.GetAlphamaps(terX, terY, areaOfEffectSize, areaOfEffectSize);
for(int xx = 0; xx < areaOfEffectSize; xx++)
{
for (int yy = 0; yy < areaOfEffectSize; yy++)
{
splatmapData[yy, xx, 1] = element[0, 0, 1] = 1;
}
}
terrain.terrainData.SetAlphamaps(terX, terY, element);
}
Hope sb can help me find my mistake. How can I change the size of my "brush"?
EDIT: I wrote comments into the code to see the transfered/related lines.
Oh guys, I did a stupid mistake. Solved this problem by passing the splatmapData into SetAlphamaps -.-
So the solution is:
[..]
for (int xx = 0; xx < areaOfEffectSize; xx++)
{
for (int yy = 0; yy < areaOfEffectSize; yy++)
{
splatmapData[yy, xx, 0] = 0;
splatmapData[yy, xx, 1] = 1;
}
}
terrain.terrainData.SetAlphamaps(terX, terY, splatmapData);
I am creating a game after working through a XNA 4.0 book. It will be 3D, but I am already stuck in creating the terrain...
UPDATE: Everything starting from here is an update...
Terrain Update:
public void Update(Matrix view, Matrix projection)
{
View = view;
Projection = projection;
World = Matrix.CreateTranslation(-Width / 2f, 0, Height / 2f);
}
Terrain Draw:
public void Draw(GraphicsDevice g)
{
effect.CurrentTechnique = effect.Techniques["ColoredNoShading"];
effect.Parameters["xView"].SetValue(View);
effect.Parameters["xProjection"].SetValue(Projection);
effect.Parameters["xWorld"].SetValue(World);
foreach (EffectPass pass in effect.CurrentTechnique.Passes)
{
pass.Apply();
//g.DrawUserIndexedPrimitives(PrimitiveType.TriangleList, vertices, 0, vertices.Length, indices, 0, indices.Length / 3, VertexPositionColorNormal.VertexDeclaration);
g.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, vertices.Length, 0, indices.Length / 3);
}
}
The commented line is working, in the both cases I am able to see the terrain...
The following code is to initialize Vertex and Index Buffer:
private void SetUpVertices(GraphicsDevice g)
{
float currentH;
int currentI;
vertices = new VertexPositionColorNormal[Width * Height];
for (int x = 0; x < Width; x++)
{
for (int y = 0; y < Height; y++)
{
currentH = heightData[x,y];
currentI = x + y * Width;
vertices[currentI].Position = new Vector3(x, currentH , -y);
if (currentH < minH + (maxH - minH) / 3)
vertices[currentI].Color = Color.ForestGreen;
else if (currentH < maxH - (maxH - minH) / 3)
vertices[currentI].Color = Color.LawnGreen;
else
vertices[currentI].Color = Color.White;
}
}
SetUpIndices(g);
}
private void SetUpIndices(GraphicsDevice g)
{
indices = new int[(Width - 1) * (Height - 1) * 6];
int counter = 0;
for (int y = 0; y < Height - 1; y++)
{
for (int x = 0; x < Width - 1; x++)
{
int lowerLeft = x + y * Width;
int lowerRight = (x + 1) + y * Width;
int topLeft = x + (y + 1) * Width;
int topRight = (x + 1) + (y + 1) * Width;
indices[counter++] = topLeft;
indices[counter++] = lowerRight;
indices[counter++] = lowerLeft;
indices[counter++] = topLeft;
indices[counter++] = topRight;
indices[counter++] = lowerRight;
}
}
SetUpNormals(g);
}
private void SetUpNormals(GraphicsDevice g)
{
for (int i = 0; i < vertices.Length; i++)
{
vertices[i].Normal = Vector3.Zero;
}
int[] index = new int[3];
Vector3 s1, s2, n;
for (int i = 0; i < vertices.Length / 3; i++)
{
for (int y = 0; y < 3; y++)
index[y] = indices[i * 3 + y];
s1 = vertices[index[0]].Position - vertices[index[2]].Position;
s2 = vertices[index[0]].Position - vertices[index[1]].Position;
n = Vector3.Cross(s1, s2);
for (int y = 0; y < 3; y++)
{
vertices[index[y]].Normal += n;
vertices[index[y]].Normal.Normalize();
}
}
FillBuffers(g);
}
private void FillBuffers(GraphicsDevice g)
{
VertexBuffer = new VertexBuffer(g, VertexPositionColorNormal.VertexDeclaration, vertices.Length, BufferUsage.WriteOnly);
VertexBuffer.SetData(vertices);
IndexBuffer = new IndexBuffer(g, typeof(int), indices.Length, BufferUsage.WriteOnly);
IndexBuffer.SetData(indices);
g.Indices = IndexBuffer;
g.SetVertexBuffer(VertexBuffer);
}
I don't think, that there is a mistake, because it is working with the other line. Might there be an error with the .fx file I am using. If you think so, I am going to switch to BasicEffects...
(You might notice, that the code is from http://www.riemers.net/eng/Tutorials/XNA/Csharp/series1.php )
Thanks for your help...
Yours,
Florian
(Answer to original revision of the question.)
You're not setting your vertex buffer and index buffer onto the graphics device. These two lines of code (untested) should do what you need:
g.GraphicsDevice.Indices = indexBuffer;
g.GraphicsDevice.SetVertexBuffer(vertexBuffer);
Place them just after you set the parameters on your effect (ef), before the loop.
The vertex buffer provides the vertex declaration that the exception message is asking for.
Edit after question update: In your new version you're setting the vertex and index buffers - but it's in the wrong place. You need to set them onto the graphics device each frame. Your code would only work if nothing changes them after you set them in FillBuffers. But I'm guessing that stuff is being drawn outside your class's Draw method?
If that something else is a SpriteBatch, even it works using vertex buffers and index buffers. So it will reset your settings. (It's worth adding that it also sets render states - in which case you might need to see this article.)