12 Replies - 2744 Views - Last Post: 07 October 2013 - 05:54 AM Rate Topic: -----

#1 Toadill  Icon User is offline

  • D.I.C Regular

Reputation: 45
  • View blog
  • Posts: 392
  • Joined: 08-January 12

Unity C# Terrain Scripting

Posted 16 September 2013 - 08:13 AM

I am currently trying to make a chess board like grid. So far I have done this with objects but I would like to do this with sections of terrain instead. Is there a way to do this with terrain?

Here is the code I have so far as far as doing this with a cube prefab.
using UnityEngine;
using System.Collections;

public class Cell : MonoBehaviour {
	
	public Transform CellPrefab;
	public Vector3 Size;
	
	// Use this for initialization
	void Start () 
	{
		CreateGrid();
	}
	
	void CreateGrid()
	{
		for (int x = 0; x < Size.x; x++)
		{
			for (int z = 0; z < Size.z; z++)
			{
				Transform newCell;
				newCell = (Transform)Instantiate(CellPrefab, new Vector3(x, 0, z),Quaternion.identity);
				newCell.parent = transform;
			}
		}
	}
}


This post has been edited by Toadill: 16 September 2013 - 08:14 AM


Is This A Good Question/Topic? 0
  • +

Replies To: Unity C# Terrain Scripting

#2 BBeck  Icon User is offline

  • Here to help.
  • member icon


Reputation: 560
  • View blog
  • Posts: 1,263
  • Joined: 24-April 12

Re: Unity C# Terrain Scripting

Posted 16 September 2013 - 08:30 AM

Can't you do this with a simple plane model? If you had a texture that has 4 squares of alternating colors, you could UV Wrap them as many times as needed across a plane object.
Was This Post Helpful? 0
  • +
  • -

#3 Toadill  Icon User is offline

  • D.I.C Regular

Reputation: 45
  • View blog
  • Posts: 392
  • Joined: 08-January 12

Re: Unity C# Terrain Scripting

Posted 16 September 2013 - 09:10 AM

I more less just wanted to include a grid on the terrain that I had already created. I am a beginner in Unity, so I have no idea what you mean by UV. The idea is to keep the bump map attributes as well as terrain textures.
The game will have game pieces like chess that will be able to move and I would like to make it so that when the player selects a piece with the mouse it will highlight the allowed path or the amount of allow squares on the grid. I plan to write a script that will use the grid to move the pieces as well.

This post has been edited by Toadill: 16 September 2013 - 09:11 AM

Was This Post Helpful? 0
  • +
  • -

#4 BBeck  Icon User is offline

  • Here to help.
  • member icon


Reputation: 560
  • View blog
  • Posts: 1,263
  • Joined: 24-April 12

Re: Unity C# Terrain Scripting

Posted 16 September 2013 - 12:19 PM

I'm having difficulty picturing what you are talking about.

By "terrain" I assume you mean something like this where you have something like hills and valleys but basically based off of a grid. The terrain in that picture is wireframe; a normal terrain would have a grass texture on it.

If you are trying to make the grid lines visable and highlight a selected "square", I can see where my suggestion would not work.

So, the way I imagine the problem is a grass textured terrain, where we need to make visable grid lines and highlight a selected grid square.

I would be inclined to approach this with High Level Shader Language. (I'm not even sure if you can do that without the Unity Pro version).

Without HLSL, I think what I would do is similar to what I suggested before, although I'm not sure how good the results would look. I would basically have a square grass texture and draw lines around the four edges very thinly in black or whatever color I want the grid lines to be.

Then I would apply the texture to the terrain. So, that the texture repeats across the terrain at the exact same rate as each grid square. So, if a grid square is a meter wide, I would have the texture repeat (through UV wrapping) every meter. Then you should see "grid lines" that perfectly match the shape of the terrain.

For highlighting a single grid square, I think I would create a "square" mesh the same size as a grid square. I might make it yellow, but I would use an alpha transparency to make it almost completely transparent. Then I could place it and turn it on and off to highlight a given grid square.

But I'm not sure I'm even understand your question; so I may be way off on what you are trying to do.

I just started studying Unity. Maybe if I learn it, I'll be able to give a better answer in a couple of weeks.
Was This Post Helpful? 1
  • +
  • -

#5 AdoTheLimey  Icon User is offline

  • D.I.C Head

Reputation: 19
  • View blog
  • Posts: 78
  • Joined: 28-June 10

Re: Unity C# Terrain Scripting

Posted 16 September 2013 - 06:54 PM

I would create both a grid and terrain and overlay the terrain. The grid would not be rendered and would handle all the logic and the movement. The terrain itself does not need to know how to do any of that stuff. Just make sure the origin of the grid and the origin of the terrain match.

I wrote a blog post about this not too long ago, feel free to study/use my code 2D Grid on a 3D Terrain Video too if you want to see the dancing skeletons in action :P Dancing Skeletons!

To highlight specific squares on your terrain, I would use a square texture and a Projector component. Dont worry about that though until you have your pathfinding and other grid logic setup.
Was This Post Helpful? 2
  • +
  • -

#6 Toadill  Icon User is offline

  • D.I.C Regular

Reputation: 45
  • View blog
  • Posts: 392
  • Joined: 08-January 12

Re: Unity C# Terrain Scripting

Posted 16 September 2013 - 08:11 PM

I have this in a empty game object but it don't seem to be working for me. :bigsmile:

using UnityEngine;
using System.Collections;

public class CursorGrid : MonoBehaviour 
{
	
	public Terrain terrain;
	private TerrainData terrainData;
	private Vector3 terrainSize;
	private int heightmapWidth;
	private int heightmapHeight;
	private float[,] heightmapData;
	

	
	void GetTerrainData() 
	{
	    if ( !terrain )
	    {
	       terrain = Terrain.activeTerrain;
	    }
	 
	    terrainData = terrain.terrainData;
	 
	    terrainSize = terrain.terrainData.size;
	 
	    heightmapWidth = terrain.terrainData.heightmapWidth;
	    heightmapHeight = terrain.terrainData.heightmapHeight;
	 
	    heightmapData = terrainData.GetHeights( 0, 0, heightmapWidth, heightmapHeight );
	}
	
	private Vector3 rayHitPoint;
	private Vector3 heightmapPos;
	
	// Use this for initialization
	void Start () {
		GetTerrainData();
    	ConstructMesh();
	}
	
	// Update is called once per frame
	void Update () {
		// raycast to the terrain
	    RaycastToTerrain();
	 
	    // find the heightmap position of that hit
	    GetHeightmapPosition();
	 
	    // Calculate Grid
	    CalculateGrid();
	 
	    // Update Mesh
	    UpdateMesh();
	}
	
	void RaycastToTerrain() 
	{
	    RaycastHit hit;
	    Ray rayPos = (Ray)Camera.mainCamera.ScreenPointToRay( Input.mousePosition );
	 
	    if ( Physics.Raycast( rayPos, out hit, Mathf.Infinity ) ) // also consider a layermask to just the terrain layer
	    {
	       Debug.DrawLine( Camera.mainCamera.transform.position, hit.point, Color.red );
	       rayHitPoint = hit.point;
	    }
	}
	 
	 
	void GetHeightmapPosition() 
	{
	    // find the heightmap position of that hit
	    heightmapPos.x = ( rayHitPoint.x / terrainSize.x ) * ((float) heightmapWidth );
	    heightmapPos.z = ( rayHitPoint.z / terrainSize.z ) * ((float) heightmapHeight );
	 
	    // convert to integer
	    heightmapPos.x = Mathf.Round( heightmapPos.x );
	    heightmapPos.z = Mathf.Round( heightmapPos.z );
	 
	    // clamp to heightmap dimensions to avoid errors
	    heightmapPos.x = Mathf.Clamp( heightmapPos.x, 0, heightmapWidth - 1 );
	    heightmapPos.z = Mathf.Clamp( heightmapPos.z, 0, heightmapHeight - 1 );
	}
	 
	 
	// --------------------------------------------------------------------------- Calculate Grid
	 
	private Vector3[,] mapGrid = new Vector3[ 9, 9 ];
	 
	public float indicatorSize = 1.0f;
	public float indicatorOffsetY = 5.0f;
	 
	 
	void CalculateGrid() 
	{
	    for ( int z = -4; z < 5; z ++ )
	    {
	       for ( int x = -4; x < 5; x ++ )
	       {
	         Vector3 calcVector;
	 
	         calcVector.x = heightmapPos.x + ( x * indicatorSize );
	         calcVector.x /= ((float) heightmapWidth ); 
	         calcVector.x *= terrainSize.x;
	 
	         float calcPosX = heightmapPos.x + ( x * indicatorSize );
	         calcPosX = Mathf.Clamp( calcPosX, 0, heightmapWidth - 1 );
	 
	         float calcPosZ = heightmapPos.z + ( z * indicatorSize );
	         calcPosZ = Mathf.Clamp( calcPosZ, 0, heightmapHeight - 1 );
	 
	         calcVector.y = heightmapData[ (int)calcPosZ, (int)calcPosX ] * terrainSize.y; // heightmapData is Y,X ; not X,Y (reversed)
	         calcVector.y += indicatorOffsetY; // raise slightly above terrain
	 
	         calcVector.z = heightmapPos.z + ( z * indicatorSize );
	         calcVector.z /= ((float) heightmapHeight ); 
	         calcVector.z *= terrainSize.z;
	 
	         mapGrid[ x + 4, z + 4 ] = calcVector;
	       }
	    }
	}
	 
	 
	// --------------------------------------------------------------------------- INDICATOR MESH
	private Mesh mesh;
	private Vector3[] verts;
	private Vector2[] uvs;
	private int[] tris;
	 
	void ConstructMesh()
	{
	    if ( !mesh )
	    {
	       	mesh = new Mesh();
			MeshFilter f = GetComponent("MeshFilter") as MeshFilter;
			f.mesh = mesh;
	    	mesh.name = gameObject.name + "Mesh";
	    }
	 
	    mesh.Clear();  
	 
	    verts = new Vector3[9 * 9]; 
	    uvs = new Vector2[9 * 9];
	    tris = new int[ 8 * 2 * 8 * 3];
	 
	    float uvStep = 1.0f / 8.0f;
	 
	    int index = 0;
	    int triIndex = 0;
	 
	    for ( int z = 0; z < 9; z ++ )
	    {
	       for ( int x = 0; x < 9; x ++ )
	       {
	         verts[ index ] = new Vector3( x, 0, z );
	         uvs[ index ] = new Vector2( ((float)x) * uvStep, ((float)z) * uvStep );
	 
	         if ( x < 8 && z < 8 )
	         {
	          tris[ triIndex + 0 ] = index + 0;
	          tris[ triIndex + 1 ] = index + 9;
	          tris[ triIndex + 2 ] = index + 1;
	 
	          tris[ triIndex + 3 ] = index + 1;
	          tris[ triIndex + 4 ] = index + 9;
	          tris[ triIndex + 5 ] = index + 10;
	 
	          triIndex += 6;
	         }
	 
	         index ++;
	       }
	    }
	 
	 
	    // - Build Mesh -
	    mesh.vertices = verts; 
	    mesh.uv = uvs;
	    mesh.triangles = tris;
	 
	    mesh.RecalculateBounds();  
	    mesh.RecalculateNormals();
	}
	 
	 
	void UpdateMesh()
	{
		
	    int index = 0;
	 
	    for ( int z = 0; z < 9; z++ )
	    {
	       for ( int x = 0; x < 9; x++ )
	       {
	         verts[index]= mapGrid[ x, z ];
	         index++;
	       }
	    }
	 
	    // assign to mesh
	    mesh.vertices = verts;
	 
	    mesh.RecalculateBounds();
	    mesh.RecalculateNormals();
	}
}




I also have done exactly as you just suggested and created a grid of a cubes and a terrain that lays over top of it.
Was This Post Helpful? 0
  • +
  • -

#7 Toadill  Icon User is offline

  • D.I.C Regular

Reputation: 45
  • View blog
  • Posts: 392
  • Joined: 08-January 12

Re: Unity C# Terrain Scripting

Posted 16 September 2013 - 09:58 PM

Okay I tried the script from the dancing skeletons and this don't seem to be working either. I have added the script to the empty object. I had to make a few changes where this was used.

obj.SetActive(ShowGrid);
//I replaced it with this
obj.SetActiveRecursively(ShowGrid);




Not sure if this will work but I have no errors in the script.
I have also assigned the terrain I created to the new empty object script.

:helpsmilie:

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class TerrainGrid : MonoBehaviour 
{

	public Terrain terrain; //terrain grid is attached to
	public bool ShowGrid = false;
	public int CellSize = 10;

	private Vector3 terrainSize;
	private Vector3 origin;
	
	private int width;
	private int height;

	private List<GameObject> objects;

	void Start ()
	{
		terrainSize = terrain.terrainData.size;
		origin = terrain.transform.position;
		
		width = (int)terrainSize.z / CellSize;
		height = (int)terrainSize.x / CellSize;
		
		objects = new List<GameObject>();
		
		BuildGrid();
	}
	
	void Update ()
	{
		foreach(GameObject obj in objects)
		{
			obj.SetActiveRecursively(ShowGrid);
		}	
	}
	
	void BuildGrid()
	{
		for(int x = 0; x < width; x++)
		{
			for(int y = 0; y < height; y++)
			{
				GameObject go = GameObject.CreatePrimitive(PrimitiveType.Cube);
				Vector3 pos = GetWorldPosition(new Vector2(x,y));
				pos += new Vector3(CellSize / 2, terrain.SampleHeight(GetWorldPosition(new Vector2(x,y))), CellSize /2);
				go.transform.position = pos;
				if(x==0)
				{
					go.renderer.material.color = Color.red;
				}
				if(y==0)
					go.renderer.material.color = Color.green;
				
				go.transform.localScale = new Vector3(CellSize /2, CellSize /2, CellSize/2);
				go.transform.parent = transform;
				go.SetActiveRecursively(false);
			
				objects.Add(go);
			}
		}
	}
	
	public Vector3 GetWorldPosition(Vector2 gridPosition)
	{
		return new Vector3(origin.z + (gridPosition.x * CellSize), origin.y, origin.x + (gridPosition.y * CellSize));
	}
	
	public Vector2 GetGridPosition(Vector3 worldPosition)
	{
		return new Vector2(worldPosition.z / CellSize, worldPosition.x / CellSize);
	}
}


Was This Post Helpful? 0
  • +
  • -

#8 Toadill  Icon User is offline

  • D.I.C Regular

Reputation: 45
  • View blog
  • Posts: 392
  • Joined: 08-January 12

Re: Unity C# Terrain Scripting

Posted 17 September 2013 - 07:59 AM

Well I have looked into this and it seems that this line of code is what is messing everything up.


//ShowGrid is a bool
obj.SetActive(ShowGrid);



Here is the error I get

Assets/Scripts/TerrainGrid.cs(39,29): error CS1061: Type `UnityEngine.GameObject' does not contain a definition for `SetActive' and no extension method `SetActive' of type `UnityEngine.GameObject' could be found (are you missing a using directive or an assembly reference?)
Was This Post Helpful? 0
  • +
  • -

#9 Toadill  Icon User is offline

  • D.I.C Regular

Reputation: 45
  • View blog
  • Posts: 392
  • Joined: 08-January 12

Re: Unity C# Terrain Scripting

Posted 17 September 2013 - 10:42 AM

Okay i figured that out I was out dated, but now I am fully up to date. I now get no error but it does not display the cube grid. I can see the cubes in the empty game object I created and they do activate by going from grey to solid black, but visually all I can see is the placement tools(x,y,z) of each cube.
Was This Post Helpful? 0
  • +
  • -

#10 AdoTheLimey  Icon User is offline

  • D.I.C Head

Reputation: 19
  • View blog
  • Posts: 78
  • Joined: 28-June 10

Re: Unity C# Terrain Scripting

Posted 17 September 2013 - 02:12 PM

View PostToadill, on 17 September 2013 - 10:42 AM, said:

Okay i figured that out I was out dated, but now I am fully up to date. I now get no error but it does not display the cube grid. I can see the cubes in the empty game object I created and they do activate by going from grey to solid black, but visually all I can see is the placement tools(x,y,z) of each cube.


SetActiveRecursively has been depreciated in Unity 4, you want to use gameObject.SetActive Also, in Unity 4, if you disable a parent object all of it's children will also be disabled. That might be what you have going on with the invisible objects.
Was This Post Helpful? 0
  • +
  • -

#11 Toadill  Icon User is offline

  • D.I.C Regular

Reputation: 45
  • View blog
  • Posts: 392
  • Joined: 08-January 12

Re: Unity C# Terrain Scripting

Posted 18 September 2013 - 06:37 AM

OMG >.< I figured the problem out the vectors literal values require the letter f for float at the end of them.

Here is the working code

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class TerrainGrid : MonoBehaviour 
{
	//terrain grid is attached to
	public Terrain terrain; 
	public bool ShowGrid = false;
	public int CellSize = 1;
	
	private Vector3 terrainSize;
	private Vector3 origin;
	
	private int width;
	private int height;

	List<GameObject> objects;

	void Start ()
	{

		terrainSize = terrain.terrainData.size;
		origin = terrain.transform.position;
		
		width = (int)terrainSize.z / CellSize;
		height = (int)terrainSize.x / CellSize;
		
		objects = new List<GameObject>();
		
		BuildGrid();
	}
	
	void Update ()
	{
		foreach(GameObject obj in objects)
		{
			obj.SetActive(ShowGrid);
		}	
	}
	
	void BuildGrid()
	{

		for(int x = 0; x < width; x++)
		{
			for(int y = 0; y < height; y++)
			{
				GameObject go = GameObject.CreatePrimitive(PrimitiveType.Cube);
				
				Vector3 pos = GetWorldPosition(new Vector2(y,x));
				pos += new Vector3(CellSize / 2f, terrain.SampleHeight(GetWorldPosition(new Vector2(y,x))), CellSize /2f);
				//set the position of the child block
				go.transform.position = pos;
				//Make the cubes red
				go.renderer.material.color = Color.red;

				//if(y==0)
					//go.renderer.material.color = Color.green;				
				
				go.transform.localScale = new Vector3(CellSize /2f, CellSize /2f, CellSize/2f);
				go.transform.parent = transform;
				name = go.transform.parent.name;
				go.SetActive(false);
				objects.Add(go);
			}
		}
	}
	
	public Vector3 GetWorldPosition(Vector2 gridPosition)
	{
		return new Vector3(origin.z + (gridPosition.x * CellSize), origin.y, origin.x + (gridPosition.y * CellSize));
	}
	
	public Vector2 GetGridPosition(Vector3 worldPosition)
	{
		return new Vector2(worldPosition.z / CellSize, worldPosition.x / CellSize);
	}
}


Was This Post Helpful? 0
  • +
  • -

#12 AdoTheLimey  Icon User is offline

  • D.I.C Head

Reputation: 19
  • View blog
  • Posts: 78
  • Joined: 28-June 10

Re: Unity C# Terrain Scripting

Posted 18 September 2013 - 09:42 AM

Glad you got it figured out. The end result of the calculation was still a whole number so I wonder why that made a difference. I'm sure someone here can educate me :)
Was This Post Helpful? 0
  • +
  • -

#13 Toadill  Icon User is offline

  • D.I.C Regular

Reputation: 45
  • View blog
  • Posts: 392
  • Joined: 08-January 12

Re: Unity C# Terrain Scripting

Posted 07 October 2013 - 05:54 AM

Because a float is not a whole number it is a decimal number.
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1