I have 4 objects each located in a corner of a square. I wish to move these objects clockwise, 1 position per method call.
With the code I have atm, they all just complete the entire loop on 1 method call instead of moving only one position...
My code so far:
void SwitchPositions()
{
tempPosition1 = parent.transform.GetChild(0).GetComponent<Transform>().position;
tempPosition2 = parent.transform.GetChild(1).GetComponent<Transform>().position;
tempPosition3 = parent.transform.GetChild(2).GetComponent<Transform>().position;
tempPosition4 = parent.transform.GetChild(3).GetComponent<Transform>().position;
parent.transform.GetChild (0).GetComponent<Transform> ().position = tempPosition2;
parent.transform.GetChild (1).GetComponent<Transform> ().position = tempPosition3;
parent.transform.GetChild (2).GetComponent<Transform> ().position = tempPosition4;
parent.transform.GetChild (3).GetComponent<Transform> ().position = tempPosition1;
Debug.Log (tempPosition1);
}
If anyone has any ideas how to fix this or at least explain to me why it-s completing the entire loop in 1 method call...
Thank you!
I am really not sure how your timer works or for that matter anything wrong with your code. But I have used the coroutine where after every two seconds the blocks get switched and it happens continuously. I think this should be somewhere close to what you need.
//Predefined positions where objects to place
public Transform[] Position;
//The objects that will will be swapped in coroutines
public Transform[] ObjectsToMove;
private int ObjectIndex = 0;
private bool startupdate = true;
void Update () {
if(startupdate)
StartCoroutine(SwitchBlocks());
}
IEnumerator SwitchBlocks() {
startupdate = false;
int tempIndex = ObjectIndex;
for(int i = 0; i < ObjectsToMove.Length; i++) {
tempIndex = ObjectIndex + i;
if(tempIndex > ObjectsToMove.Length - 1)
tempIndex -= ObjectsToMove.Length;
ObjectsToMove[i].position = Position[tempIndex].position;
}
ObjectIndex++;
if(ObjectIndex > ObjectsToMove.Length - 1) {
ObjectIndex = 0;
}
yield return new WaitForSeconds(2.0f);
startupdate = true;
yield return null;
}
Hope this helps.
Related
So I didn't find something useful, to solve my problem. My script is downloading an JSON Array from my Server and fills some text with that informations.
Thats the part of code:
void DrawUI()
{
GameObject buttonObj = transform.GetChild (0).gameObject; //Gets button to clone it
GameObject g;
int N = allCars.Length;
for (int i = 0; i < N; i++)
{
g = Instantiate(buttonObj, transform);
g.transform.Find("name").GetComponent<Text>().text = allCars[i].carName;
g.transform.Find("type").GetComponent<Text>().text = allCars[i].type;
g.transform.Find("price").GetComponent<Text>().text = allCars[i].price+ "€";
if(balance < int.Parse(allCars[i].price))
{
g.transform.Find("price").GetComponent<Text>().color = Color.red;
} else if (balance >= int.Parse(allCars[i].price))
{
g.transform.Find("price").GetComponent<Text>().color = new Color32(53, 140, 3, 255);
}
g.GetComponent<Button>().AddEventListener(i, OpenBuyDialog);
itemIndex = i;
}
Destroy(prefab);
}
This code loops and creates clones of my buttons, all fine. When user confirms the Buy Dialog, it should reload/refresh the list, but for that I have to delete the old clones. I can't find how to do that properly.
Assume the transform has only one child (buttonObj) at the beginning, then you can delete the clones with this code.
var count = transform.childCount;
for (var i = 1; i != count; ++i)
Destroy(transform.GetChild(i).gameObject);
Add your instantiates objects to a member:
private List<GameObject> buttons;
On refresh, iterate through buttons and call Destroy on each GameObject.
Since you said:
g = Instantiate(buttonObj, transform);
Your clone object is = g.
So you can basically say:
Destroy(g.gameObject);
I am making a simple quiz game for children. In which i am extracting questions from a json file. Each question will have a 10 second timer and if the button is pressed next question is displayed. the problem i am facing is that i can't seem to figure out a way to control "yeild return waitforseconds()". i have tried multiple approaches but none seems to work, it either skips the next question too fast if i try to change the value in waitforseconds(), or it gets stuck on the same question if try incrementing the questionCounter variable. I have been searching for a while a but i can't find anything. i have a deadline of tomorrow, i could really use some help.
//this is my question changing method
IEnumerator QuestionRoutin(Course c, int n)
{
while (qCounter < 3)
{
print(qCounter);
r = UnityEngine.Random.Range(0, 6);
//Question.text = c.Ques_Data[r].Quesion;
//RandomButton(c, r);
//amil = c.Ques_Data[r].Answer;
if(checkq.Count==0)
{
checkq.Add(r);
Question.text = c.Ques_Data[r].Quesion;
RandomButton(c, r);
amil = c.Ques_Data[r].Answer;
}
else if (checkq.Contains(r)==false)
{
checkq.Add(r);
Question.text = c.Ques_Data[r].Quesion;
RandomButton(c, r);
amil = c.Ques_Data[r].Answer;
}
else
{
continue;
}
if (f == true) //f is a global bool
{
f = false;
qCounter++;
//yield return new WaitForSeconds(1.0f);
}
else
{
yield return new WaitForSeconds(10);
qCounter++;
}
}
print("questions are finished");
}
//this a function attach to my buttons
public void CheckAnswer(Text button_txt)
{
//time_1 = 10.0f;
if (button_txt.text == amil)
{
flag = true;
Animator anim = Correct_Panel.GetComponentInChildren<Animator>();
if (anim != null)
{
Correct_Panel.SetActive(true);
bool isRightAnsPressed = anim.GetBool("isRightAnsPressed");
anim.SetBool("isRightAnsPressed", !isRightAnsPressed);
Invoke("disableCorrectPanel", 1.0f);
score_value = score_value + 10;
score.text = "Score: " + score_value;
//have tried all these 3 things
//StartQuestionRoutine(c1, n,true);
//qCounter++;
// flag = true;
}
}
now ik you,ll say coroutine will restart again if i call it in the button, but nothing else seems to work. how else will ik if my button is pressed or not? Please i could really use some help.
have you try to stop coroutine before to relaunch it:
private IEnumerator coroutine;
:
:
if(coroutine != null) StopCoroutine(coroutine);
coroutine = StartCoroutine(QuestionRoutin(.....));
I'm trying to understand tileSystem build in Unity, and i don't know how to stop animation in AnimatedTiles.
Once animation is started, there is no way i can think of to stop this. I'm working on Unity 2018.3.2f1, but i think that TileSystem is similar in next versions.
Only code in AnimatedTile handling animation is:
public override void GetTileData(Vector3Int location, ITilemap tileMap, ref TileData tileData)
{
tileData.transform = Matrix4x4.identity;
tileData.color = Color.white;
if (m_AnimatedSprites != null && m_AnimatedSprites.Length > 0)
{
tileData.sprite = m_AnimatedSprites[0];
tileData.colliderType = m_TileColliderType;
}
}
public override bool GetTileAnimationData(Vector3Int location, ITilemap tileMap, ref TileAnimationData tileAnimationData)
{
if (m_AnimatedSprites.Length > 0)
{
tileAnimationData.animatedSprites = m_AnimatedSprites;
tileAnimationData.animationSpeed = Random.Range(m_MinSpeed, m_MaxSpeed);
tileAnimationData.animationStartTime = m_AnimationStartTime;
return true;
}
return false;
}
I want to stop animation after some time (like 3 seconds) or after last frame. Any help would be appritiated!
So after some time a got workaround and it looks like this :
public class TileBump : MonoBehaviour
{
public Transform m_GridParent;
public GameObject m_TileMap_Prefab;
public AnimatedTile m_tilePrefabAnimated;
public Tile m_tilePrefabStatic;
private Tilemap map;
void Start()
{
StartCoroutine(EStart());
}
public IEnumerator EStart()
{
GameObject t = Instantiate(m_TileMap_Prefab, m_GridParent);
map = t.GetComponent<Tilemap>();
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
map.SetTile(new Vector3Int(i, j, 0), m_tilePrefabAnimated);
StartCoroutine(Operation(new Vector3Int(i, j, 0)));
yield return new WaitForSeconds(0.3f);
}
}
}
public IEnumerator Operation(Vector3Int x)
{
yield return new WaitForSeconds(m_tilePrefabAnimated.m_AnimatedSprites.Length / m_tilePrefabAnimated.m_AnimationSpeed);
map.SetTile(x, m_tilePrefabStatic);
}
}
BUT. What i understood here is that tiles are not for that. Every tile in TileMap refer to ScriptableObject, so every animation will be same in every frame.
However if someone need this kind of effect, its one way to do it.
I just made a png animation sequence that fires when the object is found but on the first loop it takes a lot to load, is there a way to load it faster?
The animation consist of 500 pngs loaded in a sprite the total size of all is about 180Mb, and the code that I am using is very simple:
DefaultTrackableEventHandler.cs:
public SpriteRenderer sr;
public int i = 0;
void Update ()
{
i++;
i = (i > 100) ? 0 : i;
sr.sprite = Resources.Load<Sprite> ("fractal/03_" + i.ToString ("D5"));
}
btw i'm a complete noob on unity programming so please forgive me if i'm missing something obvious, thanks
The problem is that you are loading sprite every frame. It will effect performance. Do the loading once in the Start function and store the result in an array. You can then use that array in the Update function.
const int ANIM_SIZE = 500;
Sprite[] animSprites;
public SpriteRenderer sr;
public int i = 0;
void Start()
{
for (int i = 0; i < ANIM_SIZE; i++)
{
animSprites[i] = Resources.Load<Sprite>("fractal/03_" + i.ToString("D5")) as Sprite;
}
}
void Update()
{
i++;
i = (i > 100) ? 0 : i;
sr.sprite = animSprites[i];
}
Since you are loading many files, you should use Resources.LoadAsync instead. That will let you load the data over frame and prevent any possible freezing when loading the sprites.
const int ANIM_SIZE = 500;
Sprite[] animSprites;
public SpriteRenderer sr;
public int i = 0;
bool isDoneLoading = false;
IEnumerable Start()
{
for (int i = 0; i < ANIM_SIZE; i++)
{
ResourceRequest rs = Resources.LoadAsync<Sprite>("fractal/03_" + i.ToString("D5"));
yield return rs;
animSprites[i] = rs.asset as Sprite;
}
isDoneLoading = true;
}
void Update()
{
if (isDoneLoading)
{
i++;
i = (i > 100) ? 0 : i;
sr.sprite = animSprites[i];
}
}
Finally, don't use any of these solutions mentioned above. They are just there because that's what you asked for. Use the Unity's Animation/Animator's tool to do this. You can learn more about this here.
I got some trouble with my unity cardboard app. May some of you guys can help me.
I have build a little Island with Animations and A second island as a main menu.
So when the apps starts, you see the Island from above and the Logo of the App.
When the user pull down the magnet button on side the app will starts another level.
I used this scripts:
http://www.andrewnoske.com/wiki/Unity_-_Detecting_Google_Cardboard_Click
Detecting Google Cardboard Magnetic Button Click - Singleton Implementation
CardboardMagnetSensor.cs and CardboardTriggerControlMono.cs
I created a script in my asset folder(CardboardMagnetSensor.cs) like in the description from Link. Than I created a second script(CardboardTriggerControlMono.cs) like in the discription an dragged it onto my CardboardMain in may Projekt.
The CardboardTriggerControlMono.cs looks like:
using UnityEngine;
using System.Collections;
public class CardboardTriggerControlMono : MonoBehaviour {
public bool magnetDetectionEnabled = true;
void Start() {
CardboardMagnetSensor.SetEnabled(magnetDetectionEnabled);
// Disable screen dimming:
Screen.sleepTimeout = SleepTimeout.NeverSleep;
}
void Update () {
if (!magnetDetectionEnabled) return;
if (CardboardMagnetSensor.CheckIfWasClicked()) {
Debug.Log("Cardboard trigger was just clicked");
Application.LoadLevel(1);
CardboardMagnetSensor.ResetClick();
}
}
}
The CarboardMagnetSensor:
using UnityEngine;
using System.Collections.Generic;
public class CardboardMagnetSensor {
// Constants:
private const int WINDOW_SIZE = 40;
private const int NUM_SEGMENTS = 2;
private const int SEGMENT_SIZE = WINDOW_SIZE / NUM_SEGMENTS;
private const int T1 = 30, T2 = 130;
// Variables:
private static bool wasClicked; // Flips to true once set off.
private static bool sensorEnabled; // Is sensor active.
private static List<Vector3> sensorData; // Keeps magnetic sensor data.
private static float[] offsets; // Offsets used to detect click.
// Call this once at beginning to enable detection.
public static void SetEnabled(bool enabled) {
Reset();
sensorEnabled = enabled;
Input.compass.enabled = sensorEnabled;
}
// Reset variables.
public static void Reset() {
sensorData = new List<Vector3>(WINDOW_SIZE);
offsets = new float[SEGMENT_SIZE];
wasClicked = false;
sensorEnabled = false;
}
// Poll this once every frame to detect when the magnet button was clicked
// and if it was clicked make sure to call "ResetClick()"
// after you've dealt with the action, or it will continue to return true.
public static bool CheckIfWasClicked() {
UpdateData();
return wasClicked;
}
// Call this after you've dealt with a click operation.
public static void ResetClick() {
wasClicked = false;
}
// Updates 'sensorData' and determines if magnet was clicked.
private static void UpdateData() {
Vector3 currentVector = Input.compass.rawVector;
if (currentVector.x == 0 && currentVector.y == 0 && currentVector.z == 0) {
return;
}
if(sensorData.Count >= WINDOW_SIZE) sensorData.RemoveAt(0);
sensorData.Add(currentVector);
// Evaluate model:
if(sensorData.Count < WINDOW_SIZE) return;
float[] means = new float[2];
float[] maximums = new float[2];
float[] minimums = new float[2];
Vector3 baseline = sensorData[sensorData.Count - 1];
for(int i = 0; i < NUM_SEGMENTS; i++) {
int segmentStart = 20 * i;
offsets = ComputeOffsets(segmentStart, baseline);
means[i] = ComputeMean(offsets);
maximums[i] = ComputeMaximum(offsets);
minimums[i] = ComputeMinimum(offsets);
}
float min1 = minimums[0];
float max2 = maximums[1];
// Determine if button was clicked.
if(min1 < T1 && max2 > T2) {
sensorData.Clear();
wasClicked = true; // Set button clicked to true.
// NOTE: 'wasClicked' will now remain true until "ResetClick()" is called.
}
}
private static float[] ComputeOffsets(int start, Vector3 baseline) {
for(int i = 0; i < SEGMENT_SIZE; i++) {
Vector3 point = sensorData[start + i];
Vector3 o = new Vector3(point.x - baseline.x, point.y - baseline.y, point.z - baseline.z);
offsets[i] = o.magnitude;
}
return offsets;
}
private static float ComputeMean(float[] offsets) {
float sum = 0;
foreach(float o in offsets) {
sum += o;
}
return sum / offsets.Length;
}
private static float ComputeMaximum(float[] offsets) {
float max = float.MinValue;
foreach(float o in offsets) {
max = Mathf.Max(o, max);
}
return max;
}
private static float ComputeMinimum(float[] offsets) {
float min = float.MaxValue;
foreach(float o in offsets) {
min = Mathf.Min(o, min);
}
return min;
}
}
And my steps:
http://www.directupload.net/file/d/3887/mtjygjan_jpg.htm
(sorry I´m not able to upload pictures here)
How ever, it wont work. When I start the app and pull down the magnet, nothing happens. May I did something wrong with switching the level over level index?
I use a nexus 4 and 5 for testing the app
Thanks allot and greetz to you!
Phillip
If you are using the Google Cardboard SDK for Unity, it currently has a bug that prevents Unity from seeing the magnet (and gyro, and accelerometer). That is probably why your script is not working. Until the bug is fixed, there is no good workaround, but you can instead use the property Cardboard.CardboardTriggered to detect if the magnet was pulled.
Update for Unity 5: The sensor bug is gone. Cardboard SDK does not block the sensors.