how to show images from texture array like slide shows in unity - arrays

i have a scene in which after a particular time the slide show of
images should start.
i have created the texture array and loaded the images to array in
inspector.
now i need to slide each image after every two seconds.
this is the code i have done but it is error
public Texture[] Images;
void ChangeImage()
{
for(int i=1;i<Imagez.Length;i++)
{
print (Imagez [i].ToString());
Imag =Imagez [i];
}
}
i try to load each image from the array, but i know that its not the proper way..can any one please help

Add a scroll rect then add a panel for container and make that panel the scroll rect to control.
Then the container gets a Horizontal/VerticalLayoutGroup. Add all of your images and set the container size so they show properly.
Run the game, you should be able to scroll them appropriately (make sure you constrain the movement hor/vert so it moves correctly).
private int currentIndex = 0;
[SerializeField] private float imageSize;
[SerializeField] private RectTransform panel;
[SerializeField] private float frequency;
void Start()
{
InvokeRepeating("IncreaseCurrentIndex", frequency,frequency);
}
private void IncreaseCurrentIndex()
{
if(++this.currentIndex >= this.imageCount){ this.currentIndex = 0; }
}
void MoveToNextItem()
{
float targetX = (float)this.currentIndex * this.imageSize;
Vector2 delta = this.panel.anchoredPosition;
delta.x = Mathf.MoveTowards(delta.x, targetX, Time.deltaTime * speed);
this.panel.anchoredPosition = delta;
}
First you give the size of the image in the inspector. The panel is the container for all the images. I guess the rest is self explanatory.
The code is as simple as possible, it could benefit more flexibility but at least it should go as is.
NOTE: This is meant to be for horizontal scrolling. You would change the x for y in the update for vertical scrolling. Also, it uses Mathf.MoveTowards, you could use lerp instead of the velocity of scroll rect.

Related

Winforms pie chart legend text length affects label and chartarea size

I have the following ChartArea Annotation settings set up:
private void chart1_PrePaint(object sender, ChartPaintEventArgs e)
{
if (e.ChartElement is ChartArea)
{
var ta = new TextAnnotation();
ta.IsMultiline = true;
ta.Text = "Results of Calculation\n%";
ta.Width = e.Position.Width;
ta.Height = e.Position.Height;
ta.X = e.Position.X;
ta.Y = e.Position.Y;
ta.Font = new Font("Candara", e.Position.Height / 10, FontStyle.Regular);
chart1.Annotations.Add(ta);
}
}
A few issues with this, and with the Legend in relation to my other posted question:
My other Pie Chart Legend/ChartArea question
With this PrePaint setup, I'm not sure if my position is correct for the TextAnnotation. I'm using the e.Position but it's coming out not "exactly" centered in the middle of the doughnut of the pie chart area. I'd like it to be centered perfectly. Not sure what other property to use here.
A second issue is that when Legend text length changes, it "pushes" and makes the ChartArea itself smaller so the pie chart gets smaller. I'd like it to be the other way around, where the ChartArea pie chart stays the same size but the Legend gets pushes aside.
Is this possible?
The following is the position setup of the pie chart:
Thanks
I'm sorry I couldn't help more, last time. I tested the centering of the TextAnnotation and in fact it has problems when the InnerPlotPosition is set to auto. Moreover, the answer found at link creates a new instance of the TextAnnotation at every PrePaint, causing the overlapping of TextAnnotations and the blurrying of the centered text.
I couldn't find a way to avoid the resizing of the doughnut (I'm not sure it's even possible, at this point...I'll wait for some other answers) but maybe this can work out as a workaround.
First I created a dictionary to store the centered TextAnnotations references (the key is the graph name, in case you have more than one), then in the PrePaint event I get the correct reference of the TextAnnotation used in the graph and update the coordinates of that one.
Second, I set the InnerPlotPosition manually, this seems to solve the problem of the centering of the TextAnnotation. Of course, you need to specify coordinates and size for the InnerPlot like I did with the line:
chart1.ChartAreas[0].InnerPlotPosition = new ElementPosition(0, 0, 60.65f, 94.99f);
Lastly, I set the position and the size of the legend manually and, with the extension method WrapAt I set a "line break" every _maxLegendTextBeforeWrap in the legend items text. Couldn't find a way to make it dynamically change with the width of the legend area, so it has to be set manually.
Below there's a GIF of the resulting effect. Don't know if this suits you as a solution (too much tweaking and code, for my taste), but anyway. Maybe this can trigger some new ideas on how to solve.
To do so I created these global variables:
/// <summary>
/// Saves the currently doughnut centered annotations per graph.
/// </summary>
private IDictionary<string, TextAnnotation> _annotationsByGraph;
/// <summary>
/// Number of characters
/// </summary>
private int _maxLegendTextBeforeWrap = 10;
/// <summary>
/// Legend area width.
/// </summary>
private int _legendWidth = 20;
/// <summary>
/// Legend area height.
/// </summary>
private int _legendHeight = 90;
This is the handler of the Load event:
private void ChartTest_Load(object sender, EventArgs e)
{
// ** Start of test data **
chart1.Series["Series1"].Points.AddXY("A", 33);
chart1.Series["Series1"].Points[0].LegendText = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
chart1.Series["Series1"].Points.AddXY("B", 33);
chart1.Series["Series1"].Points[1].LegendText = "BBBBBBBBBBBBBBBBBBBB";
chart1.Series["Series1"].Points.AddXY("C", 34);
chart1.Series["Series1"].Points[2].LegendText = "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC";
// ** End of test data **
// Creates a new instance of the dictionary storing the references to the annotations.
_annotationsByGraph = new Dictionary<string, TextAnnotation>();
// Createa a new instance of an annotation for the chart1 graph.
_annotationsByGraph.Add(chart1.Name, new TextAnnotation());
// Manually setting the position of the chart area prevents the imperfect positioning of the
// TextAnnotation at the center of the doughnut.
chart1.ChartAreas[0].Position.Auto = true;
// Manually set the position of the InnerPlotPosition area prevents the imperfect positioning of the
// TextAnnotation at the center of the doughnut.
chart1.ChartAreas[0].InnerPlotPosition.Auto = false;
chart1.ChartAreas[0].InnerPlotPosition = new ElementPosition(0, 0, 60.65f, 94.99f);
// Minimum size for the legend font.
chart1.Legends[0].AutoFitMinFontSize = 5;
// Set the legend style as column.
chart1.Legends[0].LegendStyle = LegendStyle.Column;
// Splits the legend texts with the space char every _maxLegendTextBeforeWrap characters.
chart1.Series["Series1"].Points.ToList().ForEach(p => p.LegendText = p.LegendText.WrapAt(_maxLegendTextBeforeWrap));
}
This is the handler of the PrePaint event:
private void chart1_PrePaint(object sender, ChartPaintEventArgs e)
{
if (e.ChartElement is ChartArea)
{
// Get the reference to the corresponding text annotation for this chart.
// We need this, otherwise we are creating and painting a new instance of a TextAnnotation
// at every PrePaint, with the resulting blurrying effect caused by the overlapping of the text.
var ta = _annotationsByGraph[e.Chart.Name];
// Check if the annotation has already been added to the chart.
if (!e.Chart.Annotations.Contains(ta))
e.Chart.Annotations.Add(ta);
// Set the properties of the centered TextAnnotation.
ta.IsMultiline = true;
ta.Text = "Results of Calculation\nx%";
ta.Font = new Font("Candara", e.Position.Height / 10, FontStyle.Regular);
ta.Width = e.Position.Width;
ta.Height = e.Position.Height;
ta.X = e.Position.X;
ta.Y = e.Position.Y;
// Move the legend manually to the right of the doughnut.
e.Chart.Legends[0].Position = new ElementPosition(e.Position.X + e.Position.Width, e.Position.Y, _legendWidth, _legendHeight);
}
}
This is what the button does:
private void BtnChangeLegendItemLength_Click(object sender, EventArgs e)
{
if (chart1.Series["Series1"].Points[1].LegendText.StartsWith("DD"))
chart1.Series["Series1"].Points[1].LegendText = "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB".WrapAt(_maxLegendTextBeforeWrap);
else
chart1.Series["Series1"].Points[1].LegendText = "DDDDDD".WrapAt(_maxLegendTextBeforeWrap);
}
This is the extension method definition:
internal static class ExtensionMethods
{
public static string WrapAt(this string legendText, int maxLengthBeforeWrap)
{
if (legendText.Length <= maxLengthBeforeWrap)
return legendText;
// Integer division to get how many times we have to insert a space.
var times = legendText.Length / maxLengthBeforeWrap;
// Counter of added spaces.
var spacesAdded = 0;
// Iterate for each space char needed.
for (var i = 1; i <= times; i++)
{
// Insert a space char every maxLengthBeforeWrap positions.
legendText = legendText.Insert(maxLengthBeforeWrap * i + spacesAdded, new string(' ', 1));
spacesAdded++;
}
return legendText;
}
}

Positioning Array Content (Sprites)

I have Pictures with Numbers on it (I mean Sprites).
I got them on an Empty GameObject, I mean [SerializeField] and added through the script (C# Ofcourse), So the Objects are not really there they are being Generated when the Game begins.
So as you can see in the Code that I can set Row and Columns Amount and with Offset also distances in X and Y Axis. But I cannot re-position it. It seems that the first one being generated is locked to the middle of the project (the first one up-Left)So I tried to move the gizmo of the empty gameobject but the sprites are still on the spot even if I use the Inspector Instead. It seems that it would need to be positioned it in the script, But How?
Please give me enough Examples witch will work with Unity?
What I tried is to position it in Unity as I already mentioned with moving the Gizmo of the Gameobject and also in the Inspector It really seems that it can only be done on the script (I might be wrong but I tried everything).
public class Controll : MonoBehaviour
{
public const int gridRows = 6;
public const int gridCols = 6;
public const float offsetX = 0.65f;
public const float offsetY = 0.97f;
[SerializeField] private GameObject[] cardBack;
// Use this for initialization
void Start ()
{
for (int i = 0; i < gridRows; i++)
{
for (int j = 0; j < gridCols; j++)
{
Instantiate(cardBack[i], new Vector3(offsetY*j, offsetX* i *-1 , -0.1f), Quaternion.identity);
}
}
}
You are instantiating all objects into the Scene root level. They are in no way related to the GameObject which was originally responsible for the instantiation.
If you rather want them to be positioned relative to the spawning GameObject then use
var position = transform.position + new Vector3(offsetY * j, offsetX * i * -1, -0.1f);
Instantiate(cardBack[i], position, Quaternion.Identity, transform);
in order to instantiate them as child objects of the GameObject this Controll script is attched to.
Now if you translate, rotate or scale that parent object all instantiated objects are transformed along with it.

Place an Image scaled at the width available space

I created a code that works, but I'm not sure that it's the best way to place an Image scaled automatically to the available width space. I need to put some content over that image, so I have a LayeredLayout: in the first layer there is the Label created with the following code, on the second layer there is a BorderLayout that has the same size of the Image.
Is the following code fine or is it possible to do better?
Label background = new Label(" ", "NoMarginNoPadding") {
boolean onlyOneTime = false;
#Override
public void paint(Graphics g) {
int labelWidth = this.getWidth();
int labelHeight = labelWidth * bgImage.getHeight() / bgImage.getWidth();
this.setPreferredH(labelHeight);
if (!onlyOneTime) {
onlyOneTime = true;
this.getParent().revalidate();
}
super.paint(g);
}
};
background.getAllStyles().setBackgroundType(Style.BACKGROUND_IMAGE_SCALED_FIT);
background.getAllStyles().setBgImage(bgImage);
Shorter code:
ScaleImageLabel sl = new ScaleImageLabel(bgImage);
sl.setUIID("Container");
You shouldn't override paint to set the preferred size. You should have overriden calcPreferredSize(). For ScaleImageLabel it's already set to the natural size of the image which should be pretty big.

Aligning a card array in-game

I'm creating a tcg (trading card game) and I would like to know how can I change the layout of the cards while playing. I mean that the cards will be spread in line aligned to the center of the screen both vertically and horizontaly, on a canvas, and when I draw/dismiss a card I would like the cards to fill in the space and align again in game. How can I do that? any ideas? I thought of a solution about when your turn begins (Start from the center of the screen then step back the length of a step X the number of cards / 2 and then spawn the cards one after another), but I can't figure out how to change the alignment of cards when you dismiss one of them without loading them all again...
Image for example
Using the same method you used for the initial position you should be able to get the new position. Now you have two positions for each card: oldPos and newPos.
Your cards are already instantiated. Their positions are stored in Transform.position. Your goal is to move from oldPos to newPos. The simplest way would be:
myCard.transform.position = newPos;
This will instantly move your cards to their new positions. However, it's not common to teleport your objects because it does not often present good feelings to users. A better solution is to smoothly move the object from a position to another.
To do this, you can move around an existing object by transform.Translate(new Vector3());, where the Vector3 will decide its moving speed. The method Translate() is doing position += movementDirection * movementAmount as you would've expected.
Moving any object over frames is called Animation. There are techniques for animation to make movements look more better (look faster than it really is, or look natural). One common method from mathematics is called linear interpolation, or lerp. Using lerp, you can easily compute intermediate points between two end-positions, and it will look natural and nice if you put your objects along the points you calculated. I believe this is what you are looking for.
========
Edit:
Here's an example of how this could be achieved. Note that Card is moving by the same amount of distance per frame in this example. Using lerp (ease-in, ease-out, etc), you could make this animation even better.
Another point I would like you to note is that I'm doing if (Vector2.Distance(nextPosition, transform.position) < 10), not if(oldPosition.equals(newPosition)). The reason is that equals() is not safe to compare floats because they are often stored as 0.4999999 and 0.50001 instead of 0.5 and 0.5. So the best way of checking floats is to test if they are "Close Enough" to each other.
Finally, you could improve the following code may improve in MANY DIFFERNET WAYS. For instnace:
Destroy() and Instantiate() is very slow operations and you
should use Object Pooling because you know you will perform these
operations constantly.
The movement of Card could be improved by better animation technique like lerp.
There may be other ways of storing List<Card> Cards
OnCardClick() is using FindObjectOfType<CardSpawner>().OnCardDeleted(this) and this requires Card to know about CardSpawner. This is called Tight Coupling, which is known as evil. There are a lot of discussions you can find why this is bad. A recommended solution would be to use event (better UnityEvent in Unity3d).
CardSpawner.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CardSpawner : MonoBehaviour
{
[SerializeField] GameObject CardParent;
[SerializeField] GameObject CardPrefab;
Vector2 DefaultSpawnPosition = new Vector2(Screen.width / 2f, Screen.height / 10f);
List<Card> Cards = new List<Card>();
public void OnClickButton()
{
SpawnNewCard();
AssignNewPositions();
AnimateCards();
}
public void OnCardDeleted(Card removedCard)
{
Cards.Remove(removedCard);
AssignNewPositions();
AnimateCards();
}
void SpawnNewCard()
{
GameObject newCard = (GameObject)Instantiate(CardPrefab, DefaultSpawnPosition, new Quaternion(), CardParent.GetComponent<Transform>());
Cards.Add(newCard.GetComponent<Card>());
}
void AssignNewPositions()
{
int n = Cards.Count;
float widthPerCard = 100;
float widthEmptySpaceBetweenCards = widthPerCard * .2f;
float totalWidthAllCards = (widthPerCard * n) + (widthEmptySpaceBetweenCards * (n-1));
float halfWidthAllCards = totalWidthAllCards / 2f;
float centreX = Screen.width / 2f;
float leftX = centreX - halfWidthAllCards;
for (int i = 0; i < n; i++)
{
if (i == 0)
Cards[i].nextPosition = new Vector2(leftX + widthPerCard / 2f, Screen.height / 2f);
else
Cards[i].nextPosition = new Vector2(leftX + widthPerCard / 2f + ((widthPerCard + widthEmptySpaceBetweenCards) * i), Screen.height / 2f);
}
}
void AnimateCards()
{
foreach (Card card in Cards)
card.StartMoving();
}
}
Card.cs
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Card : MonoBehaviour
{
public Vector2 oldPosition;
public Vector2 nextPosition;
bool IsMoving;
void Update ()
{
if (IsMoving)
{
int steps = 10;
Vector2 delta = (nextPosition - oldPosition) / steps;
transform.Translate(delta);
if (Vector2.Distance(nextPosition, transform.position) < 10)
IsMoving = false;
}
}
public void StartMoving()
{
IsMoving = true;
oldPosition = transform.position;
}
public void OnCardClick()
{
UnityEngine.Object.Destroy(this.gameObject);
Debug.Log("AfterDestroy");
FindObjectOfType<CardSpawner>().OnCardDeleted(this);
}
}

Load PNG images to array and use as textures

I've been attempting to use PNG images as textures in Unity, when I use only one or two its easy to drag and drop them in the inspector. In my current project I have over 300 images which I am trying to load into an array, I then want to change the texture each time round the update so it appears like a video.
Here is what I have so far:
using UnityEngine;
using System.Collections;
public class ChangeImage : MonoBehaviour {
public Texture[] frames;
public int CurrentFrame;
public object[] images;
void OnMouseDown() {
if (GlobalVar.PlayClip == false){
GlobalVar.PlayClip = true;
} else {
GlobalVar.PlayClip = false;
}
}
public void Start() {
images = Resources.LoadAll("Frames");
for (int i = 0; i < images.Length; i++){
Texture2D texImage = (Texture2D) images[i];
frames[i] = texImage;
}
}
// Update is called once per frame
void Update () {
if(GlobalVar.PlayClip == true){
CurrentFrame++;
CurrentFrame %= frames.Length;
GetComponent<Renderer>().material.mainTexture = frames[CurrentFrame];
}
}
}
I have been attempting to load the images into an object array convert them to textures then output to a texture array. Does anyone know where I am going wrong with this it does not seem to give any errors but the texture is not changing?
Any advice is much appreciated
Thanks
What you are doing is kinda slow and inappropriate.
What I would recommend is to use the Animator and an animation. Have all your textures into a atlas texture, this way you will limit the draw call amount. Make that texture a sprite and use the sprite editor to cut in sub sprite.
Add an animator and create an animation. Select the whole sub sprites and drag them into the animation. Done.
Now you can easily control the speed and the playing via the animator component.
I managed to fix the problem eventually, the problem seemed to be the loading of assets was not working correctly and this was why the frame was not changing. I also changed the name of the folder containing the images from "Frames" to "Resources". Here is the completed code for anyone else who needs it:
using UnityEngine;
using System.Collections;
public class ChangeImage : MonoBehaviour {
public Texture[] frames;
public int CurrentFrame;
void OnMouseDown() {
if (GlobalVar.PlayClip == false){
GlobalVar.PlayClip = true;
} else {
GlobalVar.PlayClip = false;
}
}
public void Start() {
frames = Resources.LoadAll<Texture>("");
}
// Update is called once per frame
void Update () {
if(GlobalVar.PlayClip == true){
CurrentFrame %= frames.Length;
CurrentFrame++;
Debug.Log ("Current Frame is " + CurrentFrame);
GetComponent<Renderer>().material.mainTexture = frames[CurrentFrame];
}
}
}
Thanks for the advice on animations I will still look into it as the performance of the images is not great.

Resources