Related
So, I tried to create a grid so that I can instantiate objects on it. I check for the position of said hit object (one of the squares I created) and then set the instantiated object to that position. Problem is, the squares I created with code don't have a position and are all set to 0, 0, 0.
{
GameObject tileObject = new GameObject(string.Format("{0}, {1}", x, y));
tileObject.transform.parent = transform;
Mesh mesh = new Mesh();
tileObject.AddComponent<MeshFilter>().mesh = mesh;
tileObject.AddComponent<MeshRenderer>().material = tileMaterial;
Vector3[] vertices = new Vector3[4];
vertices[0] = new Vector3(x * tileSize, 0, y * tileSize);
vertices[1] = new Vector3(x * tileSize, 0, (y +1) * tileSize);
vertices[2] = new Vector3((x +1) * tileSize, 0, y * tileSize);
vertices[3] = new Vector3((x +1) * tileSize, 0, (y +1) * tileSize);
int[] tris = new int[] { 0, 1, 2, 1, 3, 2 };
mesh.vertices = vertices;
mesh.triangles = tris;
mesh.RecalculateNormals();
tileObject.layer = LayerMask.NameToLayer("Tile");
tileObject.AddComponent<BoxCollider>();
//var xPos = Mathf.Round(x);
//var yPos = Mathf.Round(y);
//tileObject.gameObject.transform.position = new Vector3(xPos , 0f, yPos);
return tileObject;
}```
As said your issue is that you leave all tiles on the position 0,0,0 and only set their vertices to the desired world space positions.
You would rather want to keep your vertices local like e.g.
// I would use the offset of -0.5f so the mesh is centered at the transform pivot
// Also no need to recreate the arrays everytime, you can simply reference the same ones
private readonly Vector3[] vertices = new Vector3[4]
{
new Vector3(-0.5f, 0, -0.5f);
new Vector3(-0.5f, 0, 0.5f);
new Vector3(0.5f, 0, -0.5f);
new Vector3(0.5f, 0, 0.5f);
};
private readonly int[] tris = new int[] { 0, 1, 2, 1, 3, 2 };
and then in your method do
GameObject tileObject = new GameObject($"{x},{y}");
tileObject.transform.parent = transform;
tileObject.localScale = new Vector3 (tileSize, 1, tileSize);
tileObject.localPosition = new Vector3(x * tileSize, 0, y * tileSize);
The latter depends of course on your needs. Actually I would prefer to have the tiles also centered around the grid object so something like e.g.
// The "-0.5f" is for centering the tile itself correctly
// The "-gridWith/2f" makes the entire grid centered around the parent
tileObject.localPosition = new Vector3((x - 0.5f - gridWidth/2f) * tileSize, 0, (y - 0.5f - gridHeight/2f) * tileSize);
In order to later find out which tile you are standing on (e.g. via raycasts, collisions, etc) I would then rather use a dedicated component and simply tell it it's coordinates like e.g.
// Note that Tile is a built-in type so you would want to avoid confusion
public class MyTile : MonoBehaviour
{
public Vector2Int GridPosition;
}
and then while generating your grid you would simply add
var tile = tileObject.AddComponent<MyTile>();
tile.GridPosition = new Vector2Int(x,y);
while you can still also access its transform.position to get the actual world space center of the tiles
I am making a cubic voxel game. I have chunks, world, blocks and mesh generation done, but there's one problem - I could not do the texturing.
Everything I need is just add a texture to a side of a 3D mesh (Texture of every is different!). I've seen some implementations but it's hard to read somebody else's code (I've tried to use them, but it didn't work). I've tried to do this by myself, but with no results.
Can anybody explain how to do this??
Here is my current code:
[ExecuteInEditMode]
[RequireComponent(typeof(MeshFilter))]
[RequireComponent(typeof(MeshRenderer))]
public class Chunk : MonoBehaviour
{
private ushort[] _voxels = new ushort[16 * 16 * 16];
private MeshFilter meshFilter;
private Vector3[] cubeVertices = new[] {
new Vector3 (0, 0, 0),
new Vector3 (1, 0, 0),
new Vector3 (1, 1, 0),
new Vector3 (0, 1, 0),
new Vector3 (0, 1, 1),
new Vector3 (1, 1, 1),
new Vector3 (1, 0, 1),
new Vector3 (0, 0, 1),
};
private int[] cubeTriangles = new[] {
// Front
0, 2, 1,
0, 3, 2,
// Top
2, 3, 4,
2, 4, 5,
// Right
1, 2, 5,
1, 5, 6,
// Left
0, 7, 4,
0, 4, 3,
// Back
5, 4, 7,
5, 7, 6,
// Bottom
0, 6, 7,
0, 1, 6
};
public ushort this[int x, int y, int z]
{
get { return _voxels[x * 16 * 16 + y * 16 + z]; }
set { _voxels[x * 16 * 16 + y * 16 + z] = value; }
}
void Start()
{
meshFilter = GetComponent<MeshFilter>();
}
private void Update()
{
GenerateMesh();
}
public void GenerateMesh()
{
Mesh mesh = new Mesh();
List<Vector3> vertices = new List<Vector3>();
List<int> triangles = new List<int>();
for (var x = 0; x < 16; x++)
{
for (var y = 0; y < 16; y++)
{
for (var z = 0; z < 16; z++)
{
var voxelType = this[x, y, z];
if (voxelType == 0)
continue;
var pos = new Vector3(x, y, z);
var verticesPos = vertices.Count;
foreach (var vert in cubeVertices)
vertices.Add(pos + vert);
foreach (var tri in cubeTriangles)
triangles.Add(verticesPos + tri);
}
}
}
mesh.SetVertices(vertices);
mesh.SetTriangles(triangles.ToArray(), 0);
meshFilter.mesh = mesh;
}
}
NOTE: This is a repost with many edits so it is focused on one problem plus has better explanation. Sorry for that.
Like your SetVertices() and SetTriangles(), you can call a SetUVs() with a list of the UV coordinates of each vertex on your texture.
The UV list size must match the vertices list size!
The UV coordinate are expressed as Vector2 with values between 0 and 1.
For example, to apply the whole texture on the front face of your cube, you have the first 4 uvs like this:
private Vector2[] cubeUVs = new[] {
new Vector2 (0, 0),
new Vector2 (1, 0),
new Vector2 (1, 1),
new Vector2 (0, 1),
...
}
...
mesh.SetUVs(0, cubeUVs);
If your texture is not a square, then it will be stretched.
You should also call RecalculateBounds() and RecalculateNormals() at the end of your GenerateMesh() method to avoid some issues later.
EDIT
If you really want different texture files for each side of the cube, then the cleanest and most performant solution for me is to set a different VertexColor for each side of your cube, eg. (1,0,0), (0,1,0), (0,0,1), (1,1,0), (1,0,1) and (0,1,1).
However, you will have to duplicate all your vertices 3 times. (because the vertex color is bound to a vertex, and each vertex of a cube belongs to 3 sides)
(You still have to set the UVs like I said previously, but each side has the whole texture instead of only a part of the texture)
Then, you will have to create a custom shader with 6 textures in inputs (one for each side).
And in the fragment function, you select the right texture color according to the vertex color.
You can for that, do some if to select the texture, but it will be not very performant:
float3 finalColor;
if(vertexColor.r > 0.5f && vertexColor.g < 0.5f && vertexColor.b < 0.5f)
{
finalColor = text2D(_TopTexture, in.uv);
}
else if(...)
{
...
}
...
Or if you want more perf (with a lot of cubes), you can instead do some multiplications to select the right texture:
float3 topTexColor = text2D(_TopTexture, in.uv) * vertexColor.r * (1.0f - vertexColor.g) * (1.0f - vertexColor.b);
float3 frontTexColor = ...;
...
float3 finalColor = topTexColor + frontTexColor + ...;
I have written a script for a procedural cube. Basically I want to make a building whose width, length and height I can control through parameters. So by changing parameters I can make different shapes like windows and door and roof and many more. But I don't have any idea how to do that. How to reuse this script for different shapes?
using UnityEngine;
public class genralWindows: MonoBehaviour
{
public float height = 0.8 f;
public float width = 0.6 f;
public float length = 0;
void Start()
{
cube();
gameObject.GetComponent < Renderer > ().material.color = Color.white;
}
public void cube()
{
MeshFilter mf1 = GetComponent < MeshFilter > ();
Mesh mesh1 = mf1.mesh;
//vertices
Vector3[] vertices = new Vector3[]
{
//front face
new Vector3(-width, height, length), //left top front, 0
new Vector3(width, height, length), //right top front, height
new Vector3(-width, -height, length), //left bottom front, 2
new Vector3(width, -height, length), //right bottom front, width
//BACK FACE
new Vector3(width, height, -length), //right top back, 4
new Vector3(-width, height, -length), //left top back, length
new Vector3(width, -height, -length), //right bottom back, 6
new Vector3(-width, -height, -length), //left bottom back, 7
//LEFT FACE
new Vector3(-width, height, -length), //left top back,width
new Vector3(-width, height, length), //left top front,9
new Vector3(-width, -height, -length), //left bottom back,height0,
new Vector3(-width, -height, length), //left bottom front,heightheight
//RIGHT FACE
new Vector3(width, height, length), //right top front height2
new Vector3(width, height, -length), //right top back heightwidth
new Vector3(width, -height, length), //right bottom front height4
new Vector3(width, -height, -length), //right bottom back heightheight
//TOP FACE
new Vector3(-width, height, -length), //left top back height6
new Vector3(width, height, -length), //right top back height7
new Vector3(-width, height, length), //left top front heightwidth
new Vector3(width, height, length), //right top front height9
//BOTTOM FACE
new Vector3(-width, -height, length), //left bottom front 20
new Vector3(width, -height, length), //right bottom front 2height
new Vector3(-width, -height, -length), //left bottom back 22
new Vector3(width, -height, -length), //right bottom back 2width
};
//triangles// 3 points clockwise determines which side is visible
int[] triangles = new int[]
{
//front face
0,
2,
3, //first triangle
3,
1,
0, //second triangle
//back face
4,
6,
7, //first triangle
7,
5,
4, //second triangle
//left face
8,
10,
11, //first triangle
11,
9,
8, //second triangle
//right face
12,
14,
15, //first triangle
15,
13,
12, //second triangle
//top face
16,
18,
19, //first triangle
19,
17,
16, //second triangle
//bottom face
20,
22,
23, //first triangle
23,
21,
20 //second triangle
};
//UVs
Vector2[] uvs1 = new Vector2[]
{
//front face// 0,0 is bottom left, 1,1 is top right
new Vector2(0, 1),
new Vector2(0, 0),
new Vector2(1, 1),
new Vector2(1, 0),
new Vector2(0, 1),
new Vector2(0, 0),
new Vector2(1, 1),
new Vector2(1, 0),
new Vector2(0, 1),
new Vector2(0, 0),
new Vector2(1, 1),
new Vector2(1, 0),
new Vector2(0, 1),
new Vector2(0, 0),
new Vector2(1, 1),
new Vector2(1, 0),
new Vector2(0, 1),
new Vector2(0, 0),
new Vector2(1, 1),
new Vector2(1, 0),
new Vector2(0, 1),
new Vector2(0, 0),
new Vector2(1, 1),
new Vector2(1, 0)
};
mesh1.Clear();
mesh1.vertices = vertices;
mesh1.triangles = triangles;
mesh1.uv = uvs1;
mesh1.RecalculateNormals();
}
// Update is called once per frame
void Update()
{
cube();
}
}
You need to use inheritance.
Make a class Cube like this:
public class Cube:MonoBehaviour{
}
Then make other classes like this:
public class Door:Cube{
}
You can get more details here: https://unity3d.com/learn/tutorials/topics/scripting/inheritance
You don't really need to program the dimensions of a cube or other elements in your scene from scratch. Unity already offers some primitives you can instantiate in your game, and access their attributes from your script to modify their values.
In case you want to create an Enviroment programatically using GameObjects with primitives ( like cubes, spheres...) or custom GameObjects (like 3D models you created on Blender) you can do something like this:
//You will need to pass in the inspector a GameObject as parameter in the correct field
public GameObject customGameObject;
void Start()
{
generateEnviroment()
}
void generateEnviroment()
{
//You instantiate a GameObject of type cube
GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
//You change its position
cube.transform.position = new Vector3(0, 0.5F, 0);
// Widen the object by 0.1
cube.transform.localScale += new Vector3(0.1F, 0, 0);
//Change material properties, assuming it has a material component
Renderer rend = cube.GetComponent<Renderer>();
rend.material.shader = Shader.Find("Specular");
rend.material.SetColor("_SpecColor", Color.red);
//In case you want to add other type of GameObject, like a car or sth you have created:
GameObject myGameObject = GameObject.Instantiate (customGameObject);
}
You can make that function as long as complex as you want, just being tidy with names.
https://docs.unity3d.com/ScriptReference/GameObject.CreatePrimitive.html
I just tried to use VBOs. So I render a cube, and here's what's appening.
If I don't rotate it, everything is OK :
But when I rotate it, this thing appens :
It looks like the cube is translucid and... I don't really know, it's messing with my mind.
Here's my code :
internal class CubeRenderer
{
private VertexBuffer vertexBuffer;
private IndexBuffer indexBuffer;
public CubeRenderer()
{
vertexBuffer = new VertexBuffer(new[]
{
// front
new Vertex(-1.0f, -1.0f, 1.0f, Color.Red),
new Vertex(1.0f, -1.0f, 1.0f, Color.Beige),
new Vertex(1.0f, 1.0f, 1.0f, Color.SaddleBrown),
new Vertex(-1.0f, 1.0f, 1.0f, Color.AliceBlue),
//back
new Vertex(-1.0f, -1.0f, -1.0f, Color.DarkBlue),
new Vertex(1.0f, -1.0f, -1.0f, Color.Firebrick),
new Vertex(1.0f, 1.0f, -1.0f, Color.IndianRed),
new Vertex(-1.0f, 1.0f, -1.0f, Color.Yellow)
});
indexBuffer = new IndexBuffer(new uint[]
{
// front
0, 1, 2,
2, 3, 0,
// top
3, 2, 6,
6, 7, 3,
// back
7, 6, 5,
5, 4, 7,
// bottom
4, 5, 1,
1, 0, 4,
// left
4, 0, 3,
3, 7, 4,
// right
1, 5, 6,
6, 2, 1
});
}
public void Draw()
{
// 1) Ensure that the VertexArray client state is enabled.
GL.EnableClientState(ArrayCap.VertexArray);
GL.EnableClientState(ArrayCap.NormalArray);
GL.EnableClientState(ArrayCap.TextureCoordArray);
GL.EnableClientState(ArrayCap.ColorArray);
// 2) Bind the vertex and element (=indices) buffer handles.
GL.BindBuffer(BufferTarget.ArrayBuffer, vertexBuffer.Id);
GL.BindBuffer(BufferTarget.ElementArrayBuffer, indexBuffer.Id);
GL.VertexPointer(3, VertexPointerType.Float, vertexBuffer.Stride, IntPtr.Zero);
GL.NormalPointer(NormalPointerType.Float, vertexBuffer.Stride, new IntPtr(Vector3.SizeInBytes));
GL.TexCoordPointer(2, TexCoordPointerType.Float, vertexBuffer.Stride, new IntPtr(Vector3.SizeInBytes*2));
GL.ColorPointer(4, ColorPointerType.UnsignedByte, vertexBuffer.Stride, new IntPtr(Vector3.SizeInBytes*2 + Vector2.SizeInBytes));
// 4) Call DrawElements. (Note: the last parameter is an offset into the element buffer and will usually be IntPtr.Zero).
GL.DrawElements(PrimitiveType.Triangles, indexBuffer.Count, DrawElementsType.UnsignedInt, IntPtr.Zero);
//Disable client state
GL.DisableClientState(ArrayCap.VertexArray);
GL.DisableClientState(ArrayCap.NormalArray);
GL.DisableClientState(ArrayCap.TextureCoordArray);
GL.DisableClientState(ArrayCap.ColorArray);
}
}
Edit 1:
Looks like it is a problem of depth buffer. I tried to enable the DepthTest, but it still does the same thing.
Edit 2:
It might be coming from the way that I rotate the matrix...?
GL.Ortho(-Zoom * ratio, Zoom * ratio, -Zoom, Zoom, 0, 100);
Allright I found the answer by myself. The problem came from the fact that I was using glOrtho to zoom, and somehow using wrong values. I switched to glScale and everything is good now!
I am trying to create a very basic mesh renderer using D3D11 to use in my final project for school. Although I followed the basic online tutorials like the rastertek site's and Frank De Luna's book to the letter, used the simplest passthrough shader imaginable, etc, I couldn't get my triangles to show up on the screen. Finally I found out about VS 2013's graphics debugging ability, and I was able to see that my vertex and index buffers were filled with garbage data. I've hosted the solution here if you want to run the code, but can someone familiar with D3D and/or its SharpDX C# wrapper tell me what I'm doing wrong in the following code?
This is my geometry data. The Vertex struct has Vector4 position and color fields, and Index is an alias for ushort.
var vertices = new[]
{
new Vertex(new Vector4(-1, 1, 0, 1), Color.Red),
new Vertex(new Vector4(1, 1, 0, 1), Color.Green),
new Vertex(new Vector4(1, -1, 0, 1), Color.Blue),
new Vertex(new Vector4(-1, -1, 0, 1), Color.White)
};
var indices = new Index[]
{
0, 2, 1,
0, 3, 2
};
And here is the code that fails to initialize my vertex and index buffers with the above data.
var vStream = new DataStream(sizeInBytes: vertices.Length * sizeof(Vertex), canRead: false, canWrite: true);
var iStream = new DataStream(sizeInBytes: indices.Length * sizeof(Index), canRead: false, canWrite: true);
{
vStream.WriteRange(vertices);
iStream.WriteRange(indices);
vBuffer = new Buffer(
device, vStream, new BufferDescription(
vertices.Length * sizeof(Vertex),
ResourceUsage.Immutable,
BindFlags.VertexBuffer,
CpuAccessFlags.None,
ResourceOptionFlags.None,
0)) { DebugName = "Vertex Buffer" };
iBuffer = new Buffer(
device, iStream, new BufferDescription(
indices.Length * sizeof(Index),
ResourceUsage.Immutable,
BindFlags.IndexBuffer,
CpuAccessFlags.None,
ResourceOptionFlags.None,
0)) { DebugName = "Index Buffer" };
}
If I replace the above code with the following, however, it works. I have no idea what I'm doing wrong.
vBuffer = Buffer.Create(
device, vertices, new BufferDescription(
vertices.Length * sizeof(Vertex),
ResourceUsage.Immutable,
BindFlags.VertexBuffer,
CpuAccessFlags.None,
ResourceOptionFlags.None,
0));
vBuffer.DebugName = "Vertex Buffer";
iBuffer = Buffer.Create(
device, indices, new BufferDescription(
indices.Length * sizeof(Index),
ResourceUsage.Immutable,
BindFlags.IndexBuffer,
CpuAccessFlags.None,
ResourceOptionFlags.None,
0));
iBuffer.DebugName = "Index Buffer";
You need to reset the stream position to zero (like iStream.Position = 0) before passing it to new Buffer(...)