- Home /
Making a Tiled Animated Image With a Material
I'm pretty positive the answer to this is no, but I wanted to check here to make sure.
I have two classes that work with a plane to render a texture onto it. The first class "AnimatedImage.cs" uses a Material and works with the offsets and tiling to animate over the texture so that it is displayed on to the plane.
The second class "TiledImage.cs" works by using the material to wrap over a specified sized image, where it's size is set, and then via the tiling spans it accordingly across the area.
Both of them interact through the material a same way, by modifying the tiling and offset accordingly. My goal is to convert the "TiledImage.cs" into an "AnimatedTileImage.cs" so that when it tiles I can animate it as well.
Below is the code for the two classes. I'm looking for any opinions or suggestions on how to correctly perform this, if at all possible.
//---------------------------------------------------------------- START OF CLASS //----------------------------------------------------------------
using UnityEngine;
using System.Collections;
public class AnimatedImage : MonoBehaviour {
public int columnSize = 1;
public int rowSize = 1;
public int currentFrame = 0;
public int startFrame = 0;
public int endFrame = 0;
public float lastUpdateTime = 0;
public float framesPerSecond = 12;
public Material spriteTexture;
//tells to animate or not
public bool isAnimating = true;
public bool flipImage = false;
public bool animateOnce = false;
public GameObject renderPlane;
public float defaultXRotation = 0;
public float defaultYRotation = 0;
public float defaultZRotation = 0;
//Used to manage the image size, concern should be applied though, on account that
//it won't be adjusting the scale for this, so scale will still be 1, and the image may
//be far larger than that... not sure what the repercussions are for this
public float imageWidth = 1;
public float imageHeight = 1;
public float imageDepth = 1;
public virtual void Start() {
performAnimatedImageConfiguration();
//You need to call this if the image isn't animating otherwise the image displayed will be
//entire texture used. This will set the offset and size to the location on the texture/material
//being used
if(isAnimating == false) {
SetToCurrentFrameOnTexture();
}
}
public virtual void Update () {
//updates animation
if(isAnimating || animateOnce) {
UpdateAnimation();
}
}
//update the animation
public void UpdateAnimation() {
//something something helps with fps?? Maybe?? seems to work
if((Time.time - lastUpdateTime) > 1/framesPerSecond) {
lastUpdateTime = Time.time;
if(currentFrame < endFrame) {
currentFrame++;
}
if(currentFrame >= endFrame && (!animateOnce || isAnimating) ) {
currentFrame = startFrame;
}
}
SetToCurrentFrameOnTexture();
}
public void SetToCurrentFrameOnTexture() {
Vector2 size = new Vector2((float)(1.0 / columnSize), (float)(1.0 / rowSize));
// split into horizontal and vertical index
int uIndex = currentFrame % columnSize;
int vIndex = currentFrame / columnSize;
// build offset
// v coordinate is the bottom of the image in opengl so we need to invert.
Vector2 offset = new Vector2((float)(uIndex * size.x), (float)(1.0 - size.y - vIndex * size.y));
renderPlane.renderer.material.SetTextureOffset ("_MainTex", offset);
renderPlane.renderer.material.SetTextureScale ("_MainTex", size);
}
public void performAnimatedImageConfiguration() {
renderPlane = GameObject.CreatePrimitive(PrimitiveType.Plane);
DestroyImmediate(renderPlane.collider);
renderPlane.transform.parent = this.transform;
renderPlane.transform.position = this.transform.position;
renderPlane.transform.rotation = Quaternion.Euler(defaultXRotation, defaultYRotation, defaultZRotation);
renderPlane.transform.localScale = new Vector3(1, 1, 1);
configureSprite();
setFlipImage(flipImage);
configureTwoTriangleMeshFilter();
}
/*
This is a cludgy solution to a problem that I was concerned about with animated images.
The problem being that when rendering on a plane way more triangles and vertices are used
than needed. In order to circumvent this I've used a solution/example from the unity documentation
and modified it so that there aren't any issues integrating it. The problem being that it fixes a
number of weird bugs on my list... and I'm not sure why. The player no longer needs to be rotated
initially because it's handled by assumedly the normals configured in this solution. I don't
think this is a complete fix to a lot of our concerns, but I honestly feel that it will allow
us the ability to limp through the rest of our game without many concerns about wasting resources
on our animated images.
Again this is modified from the initial example, so use this link for reference if you need
a referesher:
http://docs.unity3d.com/Documentation/Manual/Example-CreatingaBillboardPlane.html
*/
public void configureTwoTriangleMeshFilter() {
//This gets the meshfilter and provides a reference to it so it can be changed
//accordingly... There is a new mesh declared because we are changing it out
//so that it is a more simple mesh rendering.
MeshFilter mf = renderPlane.GetComponent<MeshFilter>();
Mesh mesh = new Mesh();
mf.mesh = mesh;
//This configures the amount of vertices used on the plane, making it so that
//there are just four points used for rending. The width and height are used
//as a way to offset correctly from the center point of the game object being used
Vector3[] vertices = new Vector3[4];
vertices[0] = new Vector3(-imageWidth/2, -imageHeight/2, 0);
vertices[1] = new Vector3(imageWidth/2, -imageHeight/2, 0);
vertices[2] = new Vector3(-imageWidth/2, imageHeight/2, 0);
vertices[3] = new Vector3(imageWidth/2, imageHeight/2, 0);
mesh.vertices = vertices;
//This configures the triangles to be used for rendering and associates them
//with the vertices declared above
int[] tri = new int[6];
//triangle one
tri[0] = 0;
tri[1] = 2;
tri[2] = 1;
//triangle two
tri[3] = 2;
tri[4] = 3;
tri[5] = 1;
mesh.triangles = tri;
//This sets the normal of the plane to face the direction desired. I used the forward
//normal on account that it's consistent with having x in the positive direction and y
//in the positive direction in the scene view, you can set this to however you wish
Vector3[] normals = new Vector3[4];
normals[0] = Vector3.forward;
normals[1] = Vector3.forward;
normals[2] = Vector3.forward;
normals[3] = Vector3.forward;
mesh.normals = normals;
//I uh... don't really understand this part... I think it's got something to do with
//associating the scale... or something...
Vector2[] uv = new Vector2[4];
uv[0] = new Vector2(0, 0);
uv[1] = new Vector2(1, 0);
uv[2] = new Vector2(0, 1);
uv[3] = new Vector2(1, 1);
mesh.uv = uv;
}
//configures the plane texture
public void configureSprite() {
renderPlane.renderer.material = spriteTexture;
}
//offsets the cube to the plane being used so all I have to do is call this and each
//on is reset around the actual game object being tethered to
public void alignShapesToObject() {
renderPlane.transform.position = transform.position;
}
//sets animation sequence with current, start, and end frame
public void setAnimationFrames(int cFrame, int sFrame, int eFrame) {
currentFrame = cFrame;
startFrame = sFrame;
endFrame = eFrame;
}
//pass in true or false in order to set this
public void setFlipImage(bool flip) {
flipImage = flip;
//This is fucking attrocious, remove this redundancy if you're trying to make your code look better in the future
if(flipImage) {
if(renderPlane.transform.localScale.x < 0) {
Vector3 tempRenderPlane = renderPlane.renderer.transform.localScale;
tempRenderPlane.x *= -1;
renderPlane.renderer.transform.localScale = tempRenderPlane;
}
}
else {
if(renderPlane.transform.localScale.x > 0) {
Vector3 tempRenderPlane = renderPlane.renderer.transform.localScale;
tempRenderPlane.x *= -1;
renderPlane.renderer.transform.localScale = tempRenderPlane;
}
}
}
public void animateOnceAndStopAtEnd(int cFrame, int sFrame, int eFrame) {
animateOnce = true;
isAnimating = false;
setAnimationFrames(cFrame, sFrame, eFrame);
}
public void animateOver(int cFrame, int sFrame, int eFrame) {
animateOnce = false;
isAnimating = true;
setAnimationFrames(cFrame, sFrame, eFrame);
}
}
//---------------------------------------------------------------- END OF CLASS //----------------------------------------------------------------
using UnityEngine;
using System.Collections;
public class TiledImage : MonoBehaviour {
//Controls how many tiles to make
public float XScale = 1;
public float YScale = 1;
public Material spriteTexture;
public bool flipImage = false;
public GameObject renderPlane;
public float defaultXRotation = 0;
public float defaultYRotation = 0;
public float defaultZRotation = 0;
//Used to manage the image size, concern should be applied though, on account that
//it won't be adjusting the scale for this, so scale will still be 1, and the image may
//be far larger than that... not sure what the repercussions are for this
public float imageWidth = 1;
public float imageHeight = 1;
public float imageDepth = 1;
public virtual void Start() {
performAnimatedImageConfiguration();
}
public virtual void Update () {
renderPlane.transform.renderer.material.mainTextureScale = new Vector2(XScale , YScale );
}
public void performAnimatedImageConfiguration() {
renderPlane = GameObject.CreatePrimitive(PrimitiveType.Plane);
DestroyImmediate(renderPlane.collider);
renderPlane.transform.parent = this.transform;
renderPlane.transform.position = this.transform.position;
renderPlane.transform.rotation = Quaternion.Euler(defaultXRotation, defaultYRotation, defaultZRotation);
renderPlane.transform.localScale = new Vector3(1, 1, 1);
configureSprite();
setFlipImage(flipImage);
configureTwoTriangleMeshFilter();
}
/*
This is a cludgy solution to a problem that I was concerned about with animated images.
The problem being that when rendering on a plane way more triangles and vertices are used
than needed. In order to circumvent this I've used a solution/example from the unity documentation
and modified it so that there aren't any issues integrating it. The problem being that it fixes a
number of weird bugs on my list... and I'm not sure why. The player no longer needs to be rotated
initially because it's handled by assumedly the normals configured in this solution. I don't
think this is a complete fix to a lot of our concerns, but I honestly feel that it will allow
us the ability to limp through the rest of our game without many concerns about wasting resources
on our animated images.
Again this is modified from the initial example, so use this link for reference if you need
a referesher:
http://docs.unity3d.com/Documentation/Manual/Example-CreatingaBillboardPlane.html
*/
public void configureTwoTriangleMeshFilter() {
//This gets the meshfilter and provides a reference to it so it can be changed
//accordingly... There is a new mesh declared because we are changing it out
//so that it is a more simple mesh rendering.
MeshFilter mf = renderPlane.GetComponent<MeshFilter>();
Mesh mesh = new Mesh();
mf.mesh = mesh;
//This configures the amount of vertices used on the plane, making it so that
//there are just four points used for rending. The width and height are used
//as a way to offset correctly from the center point of the game object being used
Vector3[] vertices = new Vector3[4];
vertices[0] = new Vector3(-imageWidth/2, -imageHeight/2, 0);
vertices[1] = new Vector3(imageWidth/2, -imageHeight/2, 0);
vertices[2] = new Vector3(-imageWidth/2, imageHeight/2, 0);
vertices[3] = new Vector3(imageWidth/2, imageHeight/2, 0);
mesh.vertices = vertices;
//This configures the triangles to be used for rendering and associates them
//with the vertices declared above
int[] tri = new int[6];
//triangle one
tri[0] = 0;
tri[1] = 2;
tri[2] = 1;
//triangle two
tri[3] = 2;
tri[4] = 3;
tri[5] = 1;
mesh.triangles = tri;
//This sets the normal of the plane to face the direction desired. I used the forward
//normal on account that it's consistent with having x in the positive direction and y
//in the positive direction in the scene view, you can set this to however you wish
Vector3[] normals = new Vector3[4];
normals[0] = Vector3.forward;
normals[1] = Vector3.forward;
normals[2] = Vector3.forward;
normals[3] = Vector3.forward;
mesh.normals = normals;
//I uh... don't really understand this part... I think it's got something to do with
//associating the scale... or something...
Vector2[] uv = new Vector2[4];
uv[0] = new Vector2(0, 0);
uv[1] = new Vector2(1, 0);
uv[2] = new Vector2(0, 1);
uv[3] = new Vector2(1, 1);
mesh.uv = uv;
}
//configures the plane texture
public void configureSprite() {
renderPlane.renderer.material = spriteTexture;
}
//offsets the cube to the plane being used so all I have to do is call this and each
//on is reset around the actual game object being tethered to
public void alignShapesToObject() {
renderPlane.transform.position = transform.position;
}
//pass in true or false in order to set this
public void setFlipImage(bool flip) {
flipImage = flip;
//This is fucking attrocious, remove this redundancy if you're trying to make your code look better in the future
if(flipImage) {
if(renderPlane.transform.localScale.x < 0) {
Vector3 tempRenderPlane = renderPlane.renderer.transform.localScale;
tempRenderPlane.x *= -1;
renderPlane.renderer.transform.localScale = tempRenderPlane;
}
}
else {
if(renderPlane.transform.localScale.x > 0) {
Vector3 tempRenderPlane = renderPlane.renderer.transform.localScale;
tempRenderPlane.x *= -1;
renderPlane.renderer.transform.localScale = tempRenderPlane;
}
}
}
}
If I'm being too vague or this isn't being explained well enough please let me know. I'm really just looking for suggestions on avenues I can explore for this, or creative ways to get this functionality going. I can elaborate or answer questions as needed.