Texture Scanner

Hello people, i want to make a machine that will consecutive scan a the terrain in front of it and if it is a certain texture it will walk, so it will always stay on the path. How can i do that?

Use a Raycast downwards from the transform position if you want the hit texture use RaycastHit.transform.renderer.sharedMaterial.mainTexture. If you want the color data, read the mainTexture with GetPixel or GetPixelBilinear. If it’s a certain texture, or color, then translate the object.

Example 1 - Extracting a texture, comparing with others

This is a basic example of getting a texture from a RaycastHit. If it exists in a certain array of textures, we execute something.

#pragma strict

var textures : Texture2D[];
static var currentTexture : Texture2D;

function Update () {
	// Get the current texture at transform position
	currentTexture = TextureRaycast(transform.position, Vector3.down);
	
	// If the texture is within range, do something...
	if (IsWithinTextureRange(currentTexture, textures)) {
		transform.Translate(Vector3.forward*Time.deltaTime);
	}
}

// Raycast to get current texture
function TextureRaycast (pos : Vector3, dir : Vector3) : Texture2D {
	var texture : Texture2D;
	var hit : RaycastHit;
	if (Physics.Raycast(pos, dir, hit)) {
		texture = hit.transform.renderer.sharedMaterial.mainTexture as Texture2D;
	}
	return texture;
}

// See if texture is within range of approved textures
function IsWithinTextureRange (thisTexture : Texture2D, approvedTextures : Texture2D[]) : boolean {
	for (var x = 0; x<approvedTextures.Length; x++)
		if (currentTexture==approvedTextures[x])
			return true;
	return false;
}

Example 2 - Extracting a color, comparing within a range

Here we extract a color from a pixel at position and if the pixel color is within the range of colorMin and colorMax, we execute something.

#pragma strict

var colorMin : Color = Color.black;
var colorMax : Color = Color.white;
static var currentColor : Color;

function Update () {
	// Get the current color at transform position
	currentColor = GetColorAtPosition(PixelRaycast(transform.position, Vector3.down));
	
	// If the color is within range, do something...
	if (IsWithinColorRange(currentColor, colorMin, colorMax)) {
		transform.Translate(Vector3.forward*Time.deltaTime);
	}
}

// Return the texture pixel color from a PixelInfo object
function GetColorAtPosition (pixelInfo : PixelInfo) : Color {
	if (pixelInfo.texture!=null)
		return pixelInfo.texture.GetPixel(pixelInfo.position.x, pixelInfo.position.y);
	else
		return Color.black;
}

// Raycast to create a PixelInfo object
function PixelRaycast (pos : Vector3, dir : Vector3) : PixelInfo {
	var pixelInfo = new PixelInfo();
	var hit : RaycastHit;
	if (Physics.Raycast(pos, dir, hit)) {
		pixelInfo.texture = hit.transform.renderer.sharedMaterial.mainTexture as Texture2D;
		pixelInfo.position = hit.textureCoord;
	}
	return pixelInfo;
}

// See if color is within range (Example: Color.black == R0.0, G0.0, B0.0, Color.white == R1.0, G1.0, B1.0)
function IsWithinColorRange (thisColor : Color, minColor : Color, maxColor : Color) : boolean {
	return (thisColor.r>=minColor.r&&thisColor.r<=maxColor.r&&
			thisColor.g>=minColor.g&&thisColor.g<=maxColor.g&&
			thisColor.b>=minColor.b&&thisColor.b<=maxColor.b);
}

// Helper class for returning pixels from a texture
class PixelInfo {
	var texture : Texture2D;
	var position : Vector2;
}

There are some important things to keep in mind:

  1. The two methods only reads mesh colliders.

  2. Textures that is supposed to be read need Read/Write enabled in their import settings.

  3. The script doesn’t handle alternative routes, it only translates an object forward if the color or texture is within range. The reason is that such behavior is much more complex, where you would begin to script an AI. For instance, I would create a direction array if the color is “bad” below, and look for a “good” color, with where the transform came from last (to finally turn back instead of bounce in a new direction at corners). A good place to continue that is within:

    if (IsWithinColorRange(currentColor, colorMin, colorMax)) {
    // The color is good…
    } else {
    // The color is bad…
    }

  4. GetPixelAtPosition in example 2 returns black if nothing is found at position, which could lead to unwanted behavior if colorMin is black.

The thing you’re asking isn’t perfectly easy as the implementation of this requires a lot of special case scenario handling. But hopefully the example script can be a good start to something amazing.

Update: I understand you’re after comparing textures and not colors, hence the two different examples.

My guess would be :

1/ using Physics.RayCast,

2/ get the triangle id that was hit data,

3/ check by which submesh that triangle is used,

4/ get the material from that submesh (same id as the submesh in the material array),

5/ and check the mainTexture

But this is only one possible solution, maybe someone else has other suggestions…