First of all I'm not a professional coder but have done some C# in the past. I have problem with my Unity project at the moment. I'm loading images from my resources folder and rendering them on a 3D object. Problem is with my nex texture "buttons" which are supposed to look at variable "n" and determinate which image is loaded as texture on a 3D object based on their order in the images folder. Currently when I click on a next image button it renders the next texture but only for the first 2 images in the folder. After that it does go in to the loop I've checked with debug.log but it does not render next image as a texture. Code is as following:
For the next button (that calls render texture and changes variable 'n' value):
void OnMouseDown() {
if (this.name == "NextTexture") {
LevelSelect.n=+2;
LevelSelect.RenderTextures();
}
if (this.name == "PreviousTexture") {
LevelSelect.n-=2;
LevelSelect.RenderTextures();
}
and for the image to texture rendering:
public static void RenderTextures(){
Mesh mesh = staticMeshFilter.mesh;
Vector3[] vertices = mesh.vertices;
Vector2[] uvs = new Vector2[vertices.Length];
for (int i=0; i < uvs.Length; i++) {
uvs[i] = new Vector2(vertices[i].x, vertices[i].y);
}
mesh.uv = uvs;
rend.material.mainTexture = loader.LoadImages ("images") [n];
transformTexture.renderer.material.mainTextureOffset = new Vector2(0.4f, 0.28f); // +0.1x
transformTexture.renderer.material.mainTextureScale = new Vector2(0.8f, 0.8f);
}
So at the moment I'm confused about how and why my code doesn't render the 3rd and 4th image from the images folder as textures.
Cheers!
Related
I would like to find the best way (or at least a working way) to display in my UI a custom 2D shape from the position of all its points.
I have tried :
- to create a mesh (using a triangulator script to convert my (x,y) to the vertices+triangles, with a mesh renderer and a mesh filter. It seems to work but it not rendered in my UI even if its parent is a canvas (screen overlay).
- Creating a Canvasrenderer and setting my mesh inside it using SetMesh, but nothing seems to be displayed. Here is my code if it's the good way to do it :
GameObject shapeChild = new GameObject();
var shapeChildMeshRenderer = shapeChild.AddComponent<CanvasRenderer>();
shapeChildMeshRenderer.SetMaterial(material, null);
// Use the triangulator to get indices for creating triangles
Triangulator tr = new Triangulator(part.Points);
int[] indices = tr.Triangulate();
// Create the Vector3 vertices
Vector3[] vertices = new Vector3[vertices2D.Length];
for (int i = 0; i < vertices.Length; i++)
{
vertices[i] = new Vector3(vertices2D[i].x, vertices2D[i].y, 0);
}
// Create the mesh
Mesh _mesh = new Mesh();
_mesh.vertices = vertices;
_mesh.triangles = indices;
_mesh.RecalculateNormals();
_mesh.RecalculateBounds();
shapeChildMeshRenderer.SetMesh(_mesh);
shapeChild.transform.SetParent(this.transform);
I would like to keep the Screen Overlay setting.
What would be the best way to render my polygon in my overlay UI ?
I want to import and display an .obj file at runtime in Unity (2017.3.1f1) and found a fixed version (click, direct link to pastebin) of the FastObjImporter from the Unity wiki (click).
Since my .obj file doesn't include normals, I had to make the following change:
if(faceData[i].z >= 1) {
if(normals.Count != 0) {
newNormals[i] = normals[faceData[i].z - 1];
}
}
I also added mesh.RecalculateNormals(); in the beginning.
The problem and reason why I'm opening this question is that Unity doesn't display anything. The scene is pretty simply: The standard directional light and standard camera with a player character as its child.
My code (I have to rotate to align it properly):
Mesh m = FastObjImporter.Instance.ImportFile(path);
Material mat = new Material(Shader.Find("Hidden/Internal-Colored")) { color = new Color(0,0,1) };
GameObject obj = new GameObject();
if(obj.GetComponent<MeshFilter>()==null) { obj.AddComponent<MeshFilter>(); }
if(obj.GetComponent<MeshRenderer>()==null) { obj.AddComponent<MeshRenderer>(); }
obj.GetComponent<MeshFilter>().mesh = m;
obj.GetComponent<MeshRenderer>().material = mat;
obj.transform.Rotate(new Vector3(270,0,0));
obj.transform.Rotate(new Vector3(0,0,180));
I've tried different shaders and models and I'm using the same code to create my own meshes and GameObjects with vertices information I'm reading from a .txt file and everything works fine there - just not here.
I also noticed that the "triangles" list always carries 0 items, even though there are loads of "t ...." entries in my .obj file. If I change "j=1" to "j=0" here, it fills the list:
info += j;
j = 0;
while(j + 2 < info) {
In the end Unity still doesn't display anything, so there has to be something else that's wrong.
Does anyone have a suspicion what it could be and what I can do to make it work?
I'd like to get a single sprite which GameObject has in a SpriteRenderer component.
Unfortunately, this code returns the whole atlas, but I need to a part of this atlas.
Texture2D thumbnail = GetComponent<SpriteRenderer>().sprite.texture;
There is no native API to get single sprite from the SpriteRenderer and no API to access individual Sprite by name. You can vote for this feature here.
You can make your own API to get single sprite from Atlas located in the Resources folder like the image included in your question.
You can load all the sprites from the Atlas with Resources.LoadAll then store them in a dictionary.A simple function can then be used to access each sprite with the provided name.
A simple Atlas Loader script:
public class AtlasLoader
{
public Dictionary<string, Sprite> spriteDic = new Dictionary<string, Sprite>();
//Creates new Instance only, Manually call the loadSprite function later on
public AtlasLoader()
{
}
//Creates new Instance and Loads the provided sprites
public AtlasLoader(string spriteBaseName)
{
loadSprite(spriteBaseName);
}
//Loads the provided sprites
public void loadSprite(string spriteBaseName)
{
Sprite[] allSprites = Resources.LoadAll<Sprite>(spriteBaseName);
if (allSprites == null || allSprites.Length <= 0)
{
Debug.LogError("The Provided Base-Atlas Sprite `" + spriteBaseName + "` does not exist!");
return;
}
for (int i = 0; i < allSprites.Length; i++)
{
spriteDic.Add(allSprites[i].name, allSprites[i]);
}
}
//Get the provided atlas from the loaded sprites
public Sprite getAtlas(string atlasName)
{
Sprite tempSprite;
if (!spriteDic.TryGetValue(atlasName, out tempSprite))
{
Debug.LogError("The Provided atlas `" + atlasName + "` does not exist!");
return null;
}
return tempSprite;
}
//Returns number of sprites in the Atlas
public int atlasCount()
{
return spriteDic.Count;
}
}
Usage:
With the Example image above, "tile" is the base image and ball, bottom, people, and wallframe are the sprites in the atlas.
void Start()
{
AtlasLoader atlasLoader = new AtlasLoader("tiles");
Debug.Log("Atlas Count: " + atlasLoader.atlasCount());
Sprite ball = atlasLoader.getAtlas("ball");
Sprite bottom = atlasLoader.getAtlas("bottom");
Sprite people = atlasLoader.getAtlas("people");
Sprite wallframe = atlasLoader.getAtlas("wallframe");
}
You could put the image you need in the Resources folder by itself, then use the Resources.Load("spriteName") to get it. If you want to get it as a sprite, you would do the following:
Sprite thumbnail = Resources.Load("spriteName", typeof(Sprite)) as Sprite;
Source: https://forum.unity3d.com/threads/how-to-change-sprite-image-from-script.212307/
Well with the new Unity versions you can do it easily using SpriteAtlas class and GetSprite method:
https://docs.unity3d.com/ScriptReference/U2D.SpriteAtlas.html
So if you are working with the Resources folder you can do:
Resources.Load<SpriteAtlas>("AtlasName")
So I've got a little code to create a dynamic gallery that creates a master array of file locations, a loader coroutine that loads the image into a texture and a function to instantiates a prefab that takes the texture, adds it as a sprite and then parents the prefab accordingly. Finally a for-loop then repeats this process until I've run through the entire array of files and populates my gallery with all of the images. This works for a small gallery, but as my gallery grows it can get unwieldy and start grabbing massive amounts of memory. My question is how can I go about modifying this code to only load 3 images at a time (left-center-right)? I've got all my images in a scroll container and I'd like to be able to know when the images slides off-screen as a signal to load the next (or previous). Has anyone done this before that wouldn't mind sharing a little code?
public Texture2D tex;
public string[] galleryImages;
GameObject galleryThumbHolder;
string[] arctopithecusImages;
string[] arctopithecusPNGImages;
string[] gulonImages;
string[] scythianWolfImages;
string[] simivulpaImages;
string[] succorathImages;
string[] tatusImages;
int currentIndex = 0;
// Create a master Array of all image files located in all Image locations
void Start()
{
// Build Gallery Arrays
arctopithecusImages = Directory.GetFiles(#"/Users/kenmarold/Screenshots/ARCTOPITHECUS/", "*.jpg");
arctopithecusPNGImages = Directory.GetFiles(#"/Users/kenmarold/Screenshots/ARCTOPITHECUS/", "*.png");
gulonImages = Directory.GetFiles(#"/Users/kenmarold/Screenshots/GULON/", "*.jpg");
scythianWolfImages = Directory.GetFiles(#"/Users/kenmarold/Screenshots/SCYTHIAN-WOLF/", "*.png");
simivulpaImages = Directory.GetFiles(#"/Users/kenmarold/Screenshots/SIMIVULPA/", "*.png");
succorathImages = Directory.GetFiles(#"/Users/kenmarold/Screenshots/SUCCORATH/", "*.png");
tatusImages = Directory.GetFiles(#"/Users/kenmarold/Screenshots/TATUS/", "*.png");
// Concatenate all Folder Array into single Array
galleryImages =
arctopithecusImages.Concat(arctopithecusPNGImages)
.Concat(gulonImages)
.Concat(scythianWolfImages)
.Concat(simivulpaImages)
.Concat(succorathImages)
.Concat(tatusImages)
.ToArray();
for(int i = 0; i < galleryImages.Length; i++)
{
StartCoroutine("loader", currentIndex);
currentIndex++;
}
}
IEnumerator loader(int indexNum)
{
WWW www = new WWW("file://" + galleryImages[indexNum]); // get the first file from disk
yield return www; // Wait unill its loaded
tex = new Texture2D(512,512); // create a new Texture2D
www.LoadImageIntoTexture(tex); // put the image file into the new Texture2D
createGalleryImages(tex);
}
public void createGalleryImages(Texture2D tex)
{
// Instantiate Gallery Thumb Prefab and Load in Sprite
//GameObject galleryThumb = Instantiate(Resources.Load("Prefabs/GalleryImageHolder")) as GameObject;
GameObject galleryThumb = Instantiate(GameObject.FindGameObjectWithTag("GalleryImgHolder")) as GameObject;
Image galleryImg = galleryThumb.GetComponent<Image>();
Rect rct = new Rect(0, 0, tex.width, tex.height); // Define Rect arg
Vector2 pvt = new Vector2(0.5f, 0.5f); // Define Pivot arg
galleryImg.sprite = Sprite.Create(tex, rct, pvt);
// Set Gallery Thumb Parent
galleryThumb.transform.SetParent(GameObject.FindGameObjectWithTag("GalleryThumbs").transform);
}
I did something similar once, the way I implemented it, was with a trigger, that detects the entering object and get the index assigned to that object, in this case could be the index of your images in the array, this way you can know which is the current image that you are showing in the center, and can load another image if the user scrolls left or right.
Hope this help.
Ok so I ported a game I have been working on over to Monogame, however I'm having a shader issue now that it's ported. It's an odd bug, since it works on my old XNA project and it also works the first time I use it in the new monogame project, but not after that unless I restart the game.
The shader is a very simple shader that looks at a greyscale image and, based on the grey, picks a color from the lookup texture. Basically I'm using this to randomize a sprite image for an enemy every time a new enemy is placed on the screen. It works for the first time an enemy is spawned, but doesn't work after that, just giving a completely transparent texture (not a null texture).
Also, I'm only targeting Windows Desktop for now, but I am planning to target Mac and Linux at some point.
Here is the shader code itself.
sampler input : register(s0);
Texture2D colorTable;
float seed; //calculate in program, pass to shader (between 0 and 1)
sampler colorTableSampler =
sampler_state
{
Texture = <colorTable>;
};
float4 PixelShaderFunction(float2 c: TEXCOORD0) : COLOR0
{
//get current pixel of the texture (greyscale)
float4 color = tex2D(input, c);
//set the values to compare to.
float hair = 139/255; float hairless = 140/255;
float shirt = 181/255; float shirtless = 182/255;
//var to hold new color
float4 swap;
//pixel coordinate for lookup
float2 i;
i.y = 1;
//compare and swap
if (color.r >= hair && color.r <= hairless)
{
i.x = ((0.5 + seed + 96)/128);
swap = tex2D(colorTableSampler,i);
}
if (color.r >= shirt && color.r <= shirtless)
{
i.x = ((0.5 + seed + 64)/128);
swap = tex2D(colorTableSampler,i);
}
if (color.r == 1)
{
i.x = ((0.5 + seed + 32)/128);
swap = tex2D(colorTableSampler,i);
}
if (color.r == 0)
{
i.x = ((0.5 + seed)/128);
swap = tex2D(colorTableSampler, i);
}
return swap;
}
technique ColorSwap
{
pass Pass1
{
// TODO: set renderstates here.
PixelShader = compile ps_2_0 PixelShaderFunction();
}
}
And here is the function that creates the texture. I should also note that the texture generation works fine without the shader, I just get the greyscale base image.
public static Texture2D createEnemyTexture(GraphicsDevice gd, SpriteBatch sb)
{
//get a random number to pass into the shader.
Random r = new Random();
float seed = (float)r.Next(0, 32);
//create the texture to copy color data into
Texture2D enemyTex = new Texture2D(gd, CHARACTER_SIDE, CHARACTER_SIDE);
//create a render target to draw a character to.
RenderTarget2D rendTarget = new RenderTarget2D(gd, CHARACTER_SIDE, CHARACTER_SIDE,
false, gd.PresentationParameters.BackBufferFormat, DepthFormat.None);
gd.SetRenderTarget(rendTarget);
//set background of new render target to transparent.
//gd.Clear(Microsoft.Xna.Framework.Color.Black);
//start drawing to the new render target
sb.Begin(SpriteSortMode.Immediate, BlendState.Opaque,
SamplerState.PointClamp, DepthStencilState.None, RasterizerState.CullNone);
//send the random value to the shader.
Graphics.GlobalGfx.colorSwapEffect.Parameters["seed"].SetValue(seed);
//send the palette texture to the shader.
Graphics.GlobalGfx.colorSwapEffect.Parameters["colorTable"].SetValue(Graphics.GlobalGfx.palette);
//apply the effect
Graphics.GlobalGfx.colorSwapEffect.CurrentTechnique.Passes[0].Apply();
//draw the texture (now with color!)
sb.Draw(enemyBase, new Microsoft.Xna.Framework.Vector2(0, 0), Microsoft.Xna.Framework.Color.White);
//end drawing
sb.End();
//reset rendertarget
gd.SetRenderTarget(null);
//copy the drawn and colored enemy to a non-volitile texture (instead of render target)
//create the color array the size of the texture.
Color[] cs = new Color[CHARACTER_SIDE * CHARACTER_SIDE];
//get all color data from the render target
rendTarget.GetData<Color>(cs);
//move the color data into the texture.
enemyTex.SetData<Color>(cs);
//return the finished texture.
return enemyTex;
}
And just in case, the code for loading in the shader:
BinaryReader Reader = new BinaryReader(File.Open(#"Content\\shaders\\test.mgfx", FileMode.Open));
colorSwapEffect = new Effect(gd, Reader.ReadBytes((int)Reader.BaseStream.Length));
If anyone has ideas to fix this, I'd really appreciate it, and just let me know if you need other info about the problem.
I am not sure why you have "at" (#) sign in front of the string, when you escaped backslash - unless you want to have \\ in your string, but it looks strange in the file path.
You have wrote in your code:
BinaryReader Reader = new BinaryReader(File.Open(#"Content\\shaders\\test.mgfx", FileMode.Open));
Unless you want \\ inside your string do
BinaryReader Reader = new BinaryReader(File.Open(#"Content\shaders\test.mgfx", FileMode.Open));
or
BinaryReader Reader = new BinaryReader(File.Open("Content\\shaders\\test.mgfx", FileMode.Open));
but do not use both.
I don't see anything super obvious just reading through it, but really this could be tricky for someone to figure out just looking at your code.
I'd recommend doing a graphics profile (via visual studio) and capturing the frame which renders correctly then the frame rendering incorrectly and comparing the state of the two.
Eg, is the input texture what you expect it to be, are pixels being output but culled, is the output correct on the render target (in which case the problem could be Get/SetData), etc.
Change ps_2_0 to ps_4_0_level_9_3.
Monogame cannot use shaders built on HLSL 2.
Also the built in sprite batch shader uses ps_4_0_level_9_3 and vs_4_0_level_9_3, you will get issues if you try to replace the pixel portion of a shader with a different level shader.
This is the only issue I can see with your code.