Maybe there's already a way to do this, somehow, but I am missing an easy way to control the colour and brightness of the characters in 2D. 3D can do this easily with lights and lightprobes, and I was thinking that maybe lightprobes would work here, too, but they are based on lights and wouldn't give much fine control?
Perhaps something like a tint map from which you sample the colour at the character's pivot and it would be applied to any object that has the tint map script attached to it, much like objects with the sorting map scripts?
Comments
I'll open this one to the floor - there're many 2D games in development, anyone tried anything like this?
i've saved a description and the code how to do it (found on the web), but i didn't save where i found it, and who actually wrote it.. so sorry for not providing proper credit...
i hope this helps!
--stephan
----
Here's one way ...
associate a Collider with your textured plane
use Collider.Raycast to cast vertical rays (direction = Vector3.down) down into the plane from the world position of the character
use the textureCoord of the raycast hit and feed it into Texture2D.GetPixel to read the RGB value from the texture
EDIT: The following script seems to do the trick.
To use it ...
Save the script as DataField.cs in your project.
Select Game Object > Create Other > Plane from the Unity menu
Attach a material to your plane, and a texture to the material.
Check the texture importer settings on the texture and make sure Read/Write enable is set.
Attach the DataField component to your Plane object.
Find an object in your scene, or create an empty one, that will move across the plane. Attach it to the ExampleCharacterObject property of the DataField component.
Run your game, and watch the ColorUnderCharacter property of the DataField as the object moves across it. It should match the texture color under the character.
Note that another component in your scene can now have a property of type DataField, and then call the GetColorData method on it directly -- the ExampleCharacterObject lookup in the Update method is just for demonstration purposes.
------
using UnityEngine;
public class DataField : MonoBehaviour
{
// Attach something to this in the inspector and move it around
// to demonstrate the data field lookup in action.
public Transform ExampleCharacterObject = null;
// Don't modify this, it will be updated as the character object moves around.
public Color ColorUnderCharacter = Color.black;
private Texture2D mTexture = null;
// Check for required components on startup, and find the texture to be
// used for the data field.
void Start ()
{
if (collider == null)
{
Debug.LogError("collider required for DataField to perform texture lookups", gameObject);
}
if ((renderer == null) || (renderer.material == null) || (renderer.material.mainTexture == null))
{
Debug.LogError("renderer with a material and a main texture required for DataField to perform texture lookups", gameObject);
}
mTexture = renderer.material.mainTexture as Texture2D;
if (mTexture == null)
{
Debug.LogError("Texture2D required for DataField to perform texture lookups", gameObject);
}
// Note that you must turn on Read/Write enable in the import settings for the
// texture or else GetPixel will fail.
}
// Update demonstrates the use of GetColorData, but you can call
// it from elsewhere too (this behaviour doesn't need an Update
// method, this is just for illustration).
void Update ()
{
if (ExampleCharacterObject != null)
{
ColorUnderCharacter = GetColorData(ExampleCharacterObject.position);
}
}
// Find the color data under a given position.
public Color GetColorData(Vector3 position)
{
// Default to black if we find no data.
Color colorData = Color.black;
// Create a down-pointing ray at the position.
Ray ray = new Ray(position, Vector3.down);
RaycastHit hit;
// Check for a hit, using some arbitrarily long ray length.
if (collider.Raycast(ray, out hit, 10000.0f))
{
colorData = mTexture.GetPixelBilinear(hit.textureCoord.x, hit.textureCoord.y);
}
return colorData;
}
}
For full sprite tint solutions you can go with what @deroesi proposed, raycasting a texture. For this I would use a lower res texture the size (in unity units) of the background art... and test character position against it.
You could even avoid the raycasting just translading character world x/y to light texture x/y.
And If you only want to use it for a few zones, you can just use an AC trigger zone and call a custom action for tinting the character' sprite.
Ah... and @Eastman, by the way... that looks AWESOME
So in the end I came back to the "basic" lightmap solution by using the script shared by @deroesi. But I modified it to make it work the other way around. I mean I put the script on any Player/NPC sprite and I add the lightmap Plane as a parameter. This way I can easily apply the lightmap on all moving NPCs or any sprite, and you can even use a different lightmap for two characters if you want.
Here is my modified script for those interested (I put my lightmaps at Z=1 to but it behind the scene which is at Z=0, but could be higher depending of the max indicated at line 49) :
using UnityEngine; public class LightMap2D : MonoBehaviour { public MeshFilter LightMap = null; private Texture2D mTexture = null; // Check for required components on startup, and find the texture to be // used for the data field. void Start () { if (LightMap.GetComponent< Collider >() == null) { Debug.LogError("collider required for LightMap2D to perform texture lookups", gameObject); } if ((LightMap.GetComponent< Renderer >() == null) || (LightMap.GetComponent< Renderer >().material == null) || (LightMap.GetComponent< Renderer >().material.mainTexture == null)) { Debug.LogError("renderer with a material and a main texture required for LightMap2D to perform texture lookups", gameObject); } mTexture = LightMap.GetComponent< Renderer >().material.mainTexture as Texture2D; if (mTexture == null) { Debug.LogError("Texture2D required for LightMap2D to perform texture lookups", gameObject); } // Note that you must turn on Read/Write enable in the import settings for the // texture or else GetPixel will fail. } void Update () { GetComponent< SpriteRenderer >().color = GetColorData(GetComponent< Transform >().position); } // Find the color data under a given position. public Color GetColorData(Vector3 position) { // Default to black if we find no data. Color colorData = Color.black; // Create a down-pointing ray at the position. Ray ray = new Ray(new Vector3(position.x, position.y, 0), Vector3.forward); RaycastHit hit; // Check for a hit, using some arbitrarily long ray length. if (LightMap.GetComponent< Collider >().Raycast(ray, out hit, 10.0f)) { colorData = mTexture.GetPixelBilinear(hit.textureCoord.x, hit.textureCoord.y); } return colorData; } }
I'm new to unity and game dev in general so if what I did doesn't make sense I'm sorry, but it seems to work well for me
And it's still possible to combine this with the "Sprites-Diffuse" solution proposed by @Eastman
I personaly include my player prefab in every scene because when you want to change something on it in one scene it might cause inventory troubles between scenes if you include the player prefab in one scene but not the others as I described in this thread.
So as my player prefab is everywhere I can easily associate the current scene lightmap. To me it doesn't sound like a lot of work, but it depends how lazy you are
thanks for sharing your script aswell.. I took the path of putting it on the ground and not on the character because i wanted the character prefab to work in every scene without any hassle, but i'm a unity noob, so maybe this is flawed thinking...
how did you solve that? do you pass a parameter to the character when every scene starts?
Just try to add the script I shared to your character, you'll see how it works. I think it's simple, you just have to associate the lightmap to your player/NPCs in each scene. It's just few seconds of work for each scene, it is simple enough for me and having the ability to apply a lightmap to multiple characters/objects is essential in my case so I don't matter doing this small amount of extra work.
So @Gog0 Could you clarify what you mean by linking the 'lightmap plane' to your script? It seems to me you mean create a 3D Plane object and link that to the scrip, but trying that hasn't worked for me. Where can I find this mysterious lightmap plane?