- Home /
How to script a repeating environment texture
Hey Guys,
I'm working on a top-down 2.5D space combat game that has the player ship in the center of the screen, and the camera follows it as it moves around through the bigger environment.
It's a big enough environment that having a massive quad with an even more massive texture is not even close to being an option.
What I'm trying to do is to have the background texture (a grid, to track movement) that repeats. But instead of having this texture repeating across the entire environment at the same time, I want to have a 3x3 set of texture squares, with the player ship in the center. As the player ship moves from one square to another, squares are destroyed and spawned as needed to keep the player ship occupying the center square, providing the illusion of a continuous grid.
I've been wracking my brain trying to figure out how to write a C# script that would do this, but cannot figure it out.
There is a Youtube video in which the person does something similar with terrain squares, but he only provides the script with no explanation on how it works and no comment lines in the code. I've been trying to figure out how it works, but with very little knowledge of C#, I've not yet had any success in doing so.
If anyone is familiar with this type of technique and can help me to understand it, or at least point me to a tutorial (which I can not find), I would really appreciate it.
Thanks in advance.
Answer by bugmagnet · Jan 15, 2015 at 04:23 PM
Why not just use a quad and scroll the UVs ?
That does sound like a better idea, however...
I'm not seeing anything in the Inspector to adjust the UV settings. I thought it would be the $$anonymous$$esh Renderer, but there's nothing there, unless you're refering to the Offset X and Y fields. Is that what I need to adjust?
Also, I would still like to learn how to do the original thing, because I think it may be useful for another thing I'm working on.
Well the way to scroll it is either in the material, or by manually modifying the UV coordinates in the mesh.
As far as doing it the other way, I think it would be a lot more complex but certainly doable. The simplest way I think would be to lay it all out (not dynamically, but when the game starts) and then have a class that keeps track of every tile, showing the ones near the ship and hiding the others. Actually I didn't think about it before but a tool that I wrote could solve that problem by laying out the quads for you with the appropriate tiles.
That sounds like good idea. Do you know of a tutorial for this type of technique? Or if not, could you show me a brief example script?
Tiled to Unity looks interesting. Don't think I can afford that at the moment, but I'll definitely keep it in $$anonymous$$d for later.
Answer by carrollh · Jan 16, 2015 at 03:07 AM
UVs are clamped between a value of 0 and 1, but you can still set a UV value to a higher value and it will wrap around the Texture to the other side. So you might be able to get away with generating your 3x3 square by increasing the UV values dynamically. You wouldn't even need to create and destroy your quads. You could probably even do it in the shader if you were savvy enough. Just normalize your movement vector into the applicable UV translation and add it to the x and y coords in the shader program. (Maybe, that last bit is speculation. I've never done this fyi)
Does it need to work in any direction or just forward and backward?
Answer by carrollh · Jan 15, 2015 at 09:48 PM
So as far as dynamically updating the UVs goes, this works fairly well:
1)Create an empty GameObject and add this script to it. using UnityEngine; using System.Collections;
public class ShipMovement : MonoBehaviour {
public float movementSpeed = 1f;
void FixedUpdate () {
float h = Input.GetAxis("Horizontal");
float v = Input.GetAxis("Vertical");
MovementManagement(h, v);
}
void MovementManagement(float h, float v)
{
Vector3 targetDirection = new Vector3(h, 0, v);
targetDirection.Normalize();
Move(targetDirection);
}
void Move(Vector3 targetDirection)
{
transform.position += targetDirection * movementSpeed * Time.deltaTime;
}
}
2) Add your ship and your scene camera to that empty as children.
3) Create another empty, add a MeshFilter and a Mesh Renderer to it, then attach this script - (I adapted an answer from another of my posts)
using UnityEngine;
[RequireComponent(typeof(MeshFilter))]
[RequireComponent(typeof(MeshRenderer))]
public class DynamicSquare : MonoBehaviour
{
private Vector3[] vertices;
private int[] triangles;
private Vector2[] uv; // if you want to add uv coordinates
private Vector3[] normals;
void Awake()
{
/* The vertex indicies look like this, with these triangles
* 3 ------ 0
* | /|
* | / |
* | / |
* |/ |
* 2 ------ 1
*/
// the 4 vertices making up our square counterclock-wise from top right
vertices = new Vector3[4];
vertices[0] = new Vector3(1, 1, 0);
vertices[1] = new Vector3(1, 0, 0);
vertices[2] = new Vector3(0, 0, 0);
vertices[3] = new Vector3(0, 1, 0);
// list of index locations for the vertices making up each triangle
triangles = new int[6];
triangles[0] = 0;
triangles[1] = 1;
triangles[2] = 2;
triangles[3] = 0;
triangles[4] = 2;
triangles[5] = 3;
// list of UV coordinates for the corners, again counter clockwise from the top right
uv = new Vector2[4];
uv[0] = new Vector2(1, 1);
uv[1] = new Vector2(1, 0);
uv[2] = new Vector2(0, 0);
uv[3] = new Vector2(0, 1);
// list of normals for the verts
normals = new Vector3[4];
for(int i = 0; i < normals.Length; i++)
{
normals[i] = Vector3.back;
}
Mesh mesh = new Mesh();
mesh.vertices = vertices;
mesh.triangles = triangles;
mesh.uv = uv; //<-- If you want to create uvs to map to a texture
mesh.normals = normals;
mesh.name = "DynamicSquareMesh";
GetComponent<MeshFilter>().mesh = mesh;
}
}
4) scale the first empty to about .1 .1 .1, and adjust the rotations on the second empty according to the image.
5) Create a simple Material and put your texture on it. Add the material to the second empty.
6) Also add this script to the second empty (all the magic happens here. You'll need to drop the first empty into the public 'ship' variable once this script is in place)-
using UnityEngine;
using System.Collections;
public class MoveAndUpdateUVs : MonoBehaviour {
public Transform ship;
private Vector3 lastShipPos;
private Mesh mesh;
void Start()
{
lastShipPos = ship.position;
mesh = GetComponent<MeshFilter>().mesh;
}
void Update()
{
Vector3 pos = ship.position;
Vector2 shift = new Vector2(pos.x - lastShipPos.x, pos.z - lastShipPos.z);
lastShipPos = pos;
Vector2[] newUVs = mesh.uv;
for(int i = 0; i < newUVs.Length; i++)
{
newUVs[i] += shift;
}
mesh.uv = newUVs;
}
}
optional) I added a point light to the bow of my primitive ship. If you use unlit materials you won't need to obviously.
Your answer
Follow this Question
Related Questions
So, what's the problem with this code? 1 Answer
Assigning UV Map to model at runtime 0 Answers
Texture sprite goes crazy when I repeat it 1 Answer
parallax scrolling background 2 Answers
Gap Between Scrolling Background Objects 0 Answers