How To Instantiate Random Objects In A NON-SQUARE, NON-CIRCLE Area

Hello all! Thanks for reading, and I am sorry that I am foolishly new to both using Unity and C# while asking this kind of question with limited knowledge of formal terminology.

I’m trying to spawn falling objects within the parameters/boundaries(I don’t know if these are the correct words) of the room I created, but I don’t know how to properly express the boundaries(sorry I know this is another term I don’t fully grasp in C#) of the area that I want things to spawn and fall within. the room(s) that I’ve created are not squares or circles, and I don’t feel comfortable throwing a bunch of collider boxes together to express the shape of the room.

Specifically I would like to know how to instantiate objects within a specific shape of the room, or any room(which is NOT a square or circle, but rather a specific shape).

I’ve tried reading as much as I could fit in my brain, and ‘feel’ that the best of two ways is either to hardcode the parameters within a general spawning script not attached to any in-game object, but rather defined mathematically, or to ‘attach?’ the code to a poly-mesh that is created using the pro-builder tool in Unity(pictures also attached). My issue is that I don’t know how to express either scenarios beyond this point and my drawing a blank.

It was suggested that I ‘Serialize’ the coordinates of the ‘poly shape’ of the room, but after looking things up, I don’t think serializing the coordinates will help me define the parameters of the area I want the random things spawned within(at least not without further explanation). After comparing my code to others, I think(and I’m not confident in typing/saying this at all) that the red-circled area is the part of the code I need to change to define the area that I want things to spawn inside of/amongs?

I went and got the exact x/y/z coordinates that define the ‘edges’ or ‘vertices?’(point at which each line that defines the outside lines of the room shape) of the area I want things to spawn in, but I don’t know if that’s the information I need. Is there indeed a way to link what I want spawned to the shape of the room, or to a script/line that defines the boundaries of the room? I am unsure if this is the most efficient(or if it’s even a possible) way to go about it, or if I am just too lacking in basic math to understand this.

I kind of get that it wants to know the shape of the room using triangles, but I’m also struggling with interpreting that properly. would anyone be able to point in me in direction of a forum, or notes that might help me figure this out? Thanks again!

TL:DR: How do I get things to spawn in ‘custom’ area?

Edit: UgaBuga Language
-Me not trying to raycast anything
-Me not trying to have my code distribute any probability, nor distribution, amongst each individual triangle that defines the entire area
-Me not understand how to break a crazy shape into triangles in 5 seconds, but if can point me to direction to understand, me will read for several months then understand.
-Me want things to fall from sky of room, only in room not outside, but me not understand how to define room in code.
-How I convert shape, or points at edges of shape, into expression that make sense within the context of code?
-Is this the wrong place to ask for help on my code?

Sorry, had to attach the rest of my pictures here:

Sorry I had to post the rest of the pictures in each post because I am a ‘new’ user.

Sorry I had to post the rest of the pictures in each post because I am a ‘new’ user.

I mean when I google “Get random point in an arbitrary shape” I get two pretty good leads:

https://stackoverflow.com/questions/19481514/how-to-get-a-random-point-on-the-interior-of-an-irregular-polygon

https://math.stackexchange.com/questions/3319373/get-random-points-inside-custom-shape

The second being pretty simple given you have a convex shape, and the first having a good hack-job suggestion of just producing a bouncing box for the whole area, and then checking if it presides within any of the sub areas.

Another suggestion I read on another page was to have the data on each triangle, pick one at random (weighting each based on their area), and then generate a point within that triangle.

In all cases it will require a bit of maths, but also having the data of area you want to spawn in already on hand to work with. Though this could be serialised beforehand, or calculated once on startup.

1 Like

I think my own mental issue is understanding how the whole ‘triangles define every shape in your code’ thing. I get that triangles are how things are broken down easily, but I’m definitely lacking the understanding of how to ‘break shapes down to triangle’ thing. Is there something you can point me to that will help me understand this?

Edited:

I guess in laymen terms: can you point me to something/show me something that can help someone like me intuitively understand how something like a pentagon is broken down into triangles IN CODE(C#, and not just in random words/links) that is applicable to the situation? Again, I’m sorry, I am really struggling with converting terms to functions from words.

You should select a random point at ceiling height and do a raycast downwards and if it hits something then drop an item.

You don’t need to break things down into triangles in code yourself. All meshes in Unity are already comprised of triangles. You can simply pull the data from the mesh and use it from there, via the Mesh API: Unity - Scripting API: Mesh

Here I’ve whipped together a simple package that grabs the data off a mesh to generate a bunch of triangle data. Then you can press space and it spawns a sphere on a random point from a random triangle.

Note that the points are local space, so we add that point to the position of the game object. Though it doesn’t account for the object being scaled or rotated.

SurfaceSpawningExample.unitypackage (9.9 KB)

And here’s the code for anyone who doesn’t want to download the package:

using System.Collections.Generic;
using UnityEngine;

public sealed class MeshSurfaceSpawner : MonoBehaviour
{
	#region Inspector Fields

	[SerializeField]
	private MeshFilter _meshFilter;

	#endregion

	#region Internal Members

	private readonly List<Vector3> _verticies = new();

	private readonly List<int> _indicies = new();

	private readonly List<Triangle> _triangles = new();

	#endregion

	#region Unity Callbacks

	private void Awake()
	{
		var mesh = _meshFilter.sharedMesh;
		mesh.GetVertices(_verticies);
		mesh.GetIndices(_indicies, 0);

		for (int i = 0; i < _indicies.Count; i += 3)
		{
			int indexA = _indicies[i];
			int indexB = _indicies[i + 1];
			int indexC = _indicies[i + 2];

			Vector3 a = _verticies[indexA];
			Vector3 b = _verticies[indexB];
			Vector3 c = _verticies[indexC];

			var triangle = new Triangle(a, b, c);
			_triangles.Add(triangle);
		}
	}
	
	private void Update()
	{
		if (Input.GetKeyDown(KeyCode.Space))
		{
			int index = Random.Range(0, _triangles.Count);

			Triangle triangle = _triangles[index];

			Vector3 randomPoint = triangle.GetRandomPoint();

			Vector3 worldPoint = transform.position + randomPoint;

			var go = GameObject.CreatePrimitive(PrimitiveType.Sphere);
			go.transform.position = worldPoint;
		}
	}

	#endregion

	#region Nested Types

	public struct Triangle
	{
		public Vector3 A, B, C;

		public readonly Vector3 this[int index]
		{
			get
			{
				if (index < 0 || index >= 3)
				{
					throw new System.IndexOutOfRangeException(nameof(index));
				}

				return index switch
				{
					0 => A,
					1 => B,
					2 => C,
					_ => Vector3.zero
				};
			}
		}

		public Triangle(Vector3 a, Vector3 b, Vector3 c)
		{
			A = a; 
			B = b; 
			C = c;
		}

		public readonly Vector3 GetRandomPoint()
		{
			int indexA = Random.Range(0, 3);
			int indexB = (indexA + 1) % 3;
			int indexC = (indexA + 2) % 3;

			Vector3 a = this[indexA];
			Vector3 b = this[indexB];
			Vector3 c = this[indexC];

			float tAB = Random.Range(0.0f, 1.0f);
			float tAC = Random.Range(0.0f, 1.0f);

			Vector3 pointA = Vector3.Lerp(a, b, tAB);
			Vector3 pointB = Vector3.Lerp(a, c, tAC);

			float t = Random.Range(0.0f, 1.0f);
			return Vector3.Lerp(pointA, pointB, t);
		}
	}

	#endregion
}
1 Like