Why is my for loop looping infinitely - for-loop

I made two scripts in Unity3D that should check names and count of objects in folder. The thing is, that it is doing it's job infinitely. Don't you know where is the problem?
First Script
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class MenuSetup : MonoBehaviour {
public static List<Texture2D> UnitIconTextures = new List<Texture2D>();
public static List<string> UnitNames = new List<string>();
public static List<string> UnitPaths = new List<string>();
void OnGUI(){
for(int i = 0; i < UnitNames.Count; i++){
Debug.Log (UnitNames[i]);
}
}
}
Second script
using UnityEngine;
using System.Collections;
public class World : MonoBehaviour {
void Start(){
string path = "Prefabs/Units";
Object[] Units = Resources.LoadAll (path);
if(Units.Length > 0){
for(int i = 0; i < Units.Length; i++){
GameObject unit = Units[i] as GameObject;
Texture2D unitIcon = unit.GetComponent<Unit>().MenuIcon;
MenuSetup.UnitIconTextures.Add (unitIcon);
MenuSetup.UnitNames.Add (unit.name);
MenuSetup.UnitPaths.Add (path+"/"+unit.name);
}
}
}
}

Ok. Nothing is looping infinitely. The problem is with this code:
void OnGUI(){
for(int i = 0; i < UnitNames.Count; i++){
Debug.Log (UnitNames[i]);
}
}
This method might be called several times per frame. So that's why you see a lot of debug info. While Start method is called only once. So actually, in World the Start method is called once. This method adds all info to MenuSetup class only once. And then you call Debug.Log on every frame.
Read more info about OnGUI on unity doc. Also you should read about Start method here.
Little suggestion
Probably you don't need the if check in
if(Units.Length > 0){
for(int i = 0; i < Units.Length; i++){
GameObject unit = Units[i] as GameObject;
Texture2D unitIcon = unit.GetComponent<Unit>().MenuIcon;
MenuSetup.UnitIconTextures.Add (unitIcon);
MenuSetup.UnitNames.Add (unit.name);
MenuSetup.UnitPaths.Add (path+"/"+unit.name);
}
}
Because when Units.Length == 0 for-block will not be executed. So probably you want to write
for(int i = 0; i < Units.Length; i++){
GameObject unit = Units[i] as GameObject;
Texture2D unitIcon = unit.GetComponent<Unit>().MenuIcon;
MenuSetup.UnitIconTextures.Add (unitIcon);
MenuSetup.UnitNames.Add (unit.name);
MenuSetup.UnitPaths.Add (path+"/"+unit.name);
}

Related

How do I make timer change a sprite every second without using code that's redundant?

I'm not entirely sure how to phrase question so sorry if this is confusing. Anyways for context I'm making a sort of minesweeper type of game in unity and one of the things the original game had was a timer. Here it is. I want to copy that sort of thing, and while I do have code that works, it's honestly kind of redundant here's what I have .
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class Timer : MonoBehaviour
{
public float timer = 0;
public bool isStop = false;
public Image scoreCount;
public Sprite[] numbersprite;
// Start is called before the first frame update
void Start()
{
timer = 0;
isStop = true;
}
Ignore all the stuff on the top.
// Update is called once per frame
void Update()
{
if(!isStop)
{
timer += Time.deltaTime;
if(timer >= 1f)
{
scoreCount.sprite = numbersprite[1];
}
if(timer >= 2f)
{
scoreCount.sprite = numbersprite[2];
}
if(timer >= 3f)
{
scoreCount.sprite = numbersprite[3];
}
if(timer >= 4f)
{
scoreCount.sprite = numbersprite[4];
}
if(timer >= 5f)
{
scoreCount.sprite = numbersprite[5];
}
if(timer >= 6f)
{
scoreCount.sprite = numbersprite[6];
}
}
}
}
What I want is to make it so that it both displays a specific sprite after a certain amount of time has based but also not have to resort to using any of this. Is there any way I can make this work?
If you want some more information I can give you that.
This code is the ultimate solution to the problem and can support sprite indefinitely. It is also fully optimized. Just put the sprites 0 to 9 in the first list and the images in the second list respectively.
public Sprite[] spriteNumbers = new Sprite[10]; // fill with numbers
public List<Image> spriteFieds; // set Images Based on a unit of tens of hundreds
public void Start() => InvokeRepeating(nameof(SyncTimer), 0f, 1f);
public void SyncTimer()
{
for (var i = 0; i < spriteFieds.Count; i++)
{
var index = (int) (Time.time / Mathf.Pow(10, i) % 10);
spriteFieds[i].sprite = spriteNumbers[index];
}
}
How to make Stop Timer?
Here I created a standalone timer field and you can stop the step timer by pressing the Space key. You can also reset the timer with the R key, for example.
public Sprite[] spriteNumbers = new Sprite[10]; // fill with numbers
public List<Image> spriteFieds; // set timer Fields
public bool isStop;
public float timer;
public void Update()
{
if (Input.GetKeyDown(KeyCode.Space)) isStop = !isStop;
if (Input.GetKeyDown(KeyCode.R))
{
timer = 0;
SyncTimer();
}
if (!isStop)
{
timer += Time.deltaTime;
SyncTimer();
}
}
public void SyncTimer()
{
for (var i = 0; i < spriteFieds.Count; i++)
{
var index = (int) (timer / Mathf.Pow(10, i) % 10);
spriteFieds[i].sprite = spriteNumbers[index];
}
}
The Timer Result:
I'm assuming you have 10 sprites for you numbers 0-9.
numberSprite[0] would hold the sprite for "0", numberSprite[1] would hole "1", etc.
Let's say the timer is at 319.8f seconds on the back end. You would want 3 sprites to display: 3, 1, 9.
To do this, you'll need to break your timer value and sprite into the hundredths, tenths, and seconds individually. You could do this:
int timerInt = (int)Mathf.floor(timer); //Get the int value of the timer
int hundredth = (timerInt/100) % 10; // (319/100) => 3 ... (3 % 10) => 3
scoreCountHundredths.sprite = numberSprite[hundredth];
int tenth = (timerInt /10) % 10; //(319/10) => 31 ... (31 % 10) => 1
scoreCountTenths.sprite = numberSprite[tenth];
int second = timerInt % 10; // (319 % 10) => 9
scoreCountSeconds.sprite = numberSprite[second];
With the above code, your timer should correctly update to any number between 000-999 requiring only 10 sprites uploaded. Additionally, it will automatically loop if your timer goes above 999 due to the modulo (%) logic.
Warning. Coroutines or InvokeRepeating may be a trap here:
Coroutines can be used to track the time between updating the sprites, but you'll likely be wanting to tie this display directly to the in-game time. relying on coroutines to update the sprite de-couples the in-game timer from the display, as they do not have built-in catchup behaviour. If your frames are slightly delayed or lag at all, you run the risk of the time running slower when using coroutines or InvokeRepeating.
Coroutines are perfect for this. Try this code here:
public Image image;
public Sprite[] sprites;
private bool isStop = true;
private void Start()
{
isStop = false;
StartCoroutine(Timer());
}
private IEnumerator Timer()
{
while (!isStop)
{
for (int i = 0; i < sprites.Length; i++)
{
if (isStop) break;
image.sprite = sprites[i];
yield return new WaitForSeconds(1f);
}
}
}
You can convert float to int
int spriteIndex = (int)Math.Round(timer);
And used spriteIndex as index to array sprites.
Or... if you need used different time interval for every sprite, you can make special struct for this animation.
For example:
[Serializable]
public struct SpriteFrame
{
public Sprite FrameSprite;
public float TimeToShow;
}
public class SpriteAnimationComponent : MonoBehaviour
{
public Image ScoreCount;
public List<SpriteFrame> Frames = new List<SpriteFrame>();
private int _currentFrame = 0;
private float _currentPlayAnimationTime = 0f;
private bool IsPlay => _currentFrame < Frames.Count;
public void Start()
{
UpdateFrame();
}
public void Update()
{
if(!IsPlay)
return;
_currentPlayAnimationTime += Time.deltaTime;
if(NeedShowNextFrame())
ShowNextFrame();
}
private bool NeedShowNextFrame()
=> Frames[_currentFrame].TimeToShow < _currentPlayAnimationTime;
private void ShowNextFrame()
{
_currentPlayAnimationTime -= Frames[_currentFrame].TimeToShow;
_currentFrame++;
if(IsPlay)
{
UpdateFrame();
}
}
private void UpdateFrame()
{
ScoreCount.sprite = Frames[_currentFrame].FrameSprite;
}
}
You need used SerializableAttribute ([Serializable]) on SpriteFrame for show struct in Unity Inspector. In current code animation show once, but you can make it loop. For loop animation just add _currentFrame %= Frames.Count after _currentFrame++

How to rotate instantiated object to parent

I have a book case i want to fill with books so i instantiate them and it work perfectly if the bookcase was set to a rotation of (0,0,0)
As soon as i put it in place, at say a rotation of (0,45,0) the books still instantiate in a line not corresponding with the book case im sure it is something i miss please help`using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BookPlaceScript : MonoBehaviour
{
public GameObject book;
void Start()
{
float ofsetX = 0.09f;
float ofsetY = 0.18f;
float ofsetZ = 0.08f;
float bookWidth = 0.024f;
for (int i = 0; i < 60; i++)
{
GameObject theBook = Instantiate(book,
new Vector3(transform.position.x + ofsetX,
transform.position.y+ ofsetY,
transform.position.z+ ofsetZ + (i * bookWidth)),
Quaternion.Euler(0,90,0), this.transform) as GameObject;
}
}
}`

I am getting the Index was outside the bounds of the array error

I am new to coding in unity and I wrote some code that was giving me the Index was outside the bounds of the array error, but I don't know what the problem is. If you could give me some insight on what I am doing wrong that would be great.This is the code that I wrote for a game that I am working on in unity:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
public float speed = 20.5f;
public float HorizontalInput;
public float xrange = 16;
public GameObject[] projectileprefab;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.Mouse0))
{
**//This is the broken line of code**
int ProjectileIndex = Random.Range(0, projectileprefab.Length);
Instantiate(projectileprefab[ProjectileIndex], transform.position, transform.rotation);
}
transform.Translate(Vector3.right * Time.deltaTime * speed * HorizontalInput);
HorizontalInput = Input.GetAxis("Horizontal");
if (transform.position.x < -xrange)
{
transform.position = new Vector3(-xrange, transform.position.y, transform.position.z);
}
if (transform.position.x > xrange)
{
transform.position = new Vector3(xrange, transform.position.y, transform.position.z);
}
}
}
problem is:
int ProjectileIndex = Random.Range(0, projectileprefab.Length);
Instantiate(projectileprefab[ProjectileIndex], transform.position, transform.rotation)
The length of projectileprefab is zero, so it instantiates the first object, but its empty. solution: in the inspector you can set the length of the projectileprefab array and assign game objects to it.

How do I distribute cells in a grid with the new UI from script?

I'm trying to fill a grid with buttons (using UnityUI), but I want them to fill the entire screen, so I calculate it's size on the fly.
I have an empty GameObject, child of the main Canvas, with a GridLayoutGroup component, and attached to it my script:
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
public class CrearTablero: MonoBehaviour {
public int rows = 4;
public int columns = 4;
private int buttonWidth;
private int buttonHeight;
public Button prefab;
private Button button;
void Start () {
buttonHeight = Screen.height / rows;
buttonWidth = Screen.width / columns;
GridLayoutGroup grid = this.GetComponent<GridLayoutGroup> ();
grid.cellSize = new Vector2 (buttonWidth,buttonHeight);
for (int i = 0; i < rows; i++) {
for (int j = 0; j < columns; j++) {
button = (Button)Instantiate(prefab);
button.transform.SetParent(transform);
}
}
}
}
With this I've tried to make the buttons childs of the Empty GameObject and, since they are childs of a GridLayoutGroup I expected them to appear arranged properly, but when I try it, this is what I get:
What am I doing wrong?

Java for loop doesnt execute

I am having problems with my remote device discovery code for bluetooth scanning.
It scans, and prints the MAC addresses if i uncomment the "system.out.print(devicesDiscovered);
But i want to be able to extract each MAC address from the Vector and place it in a String.
I have two differant FOR loops to do this, but neither of them seem to be executing.
Code:
import java.io.IOException;
import java.util.List;
import java.util.Vector;
import javax.bluetooth.*;
public class BluetoothDeviceDiscovery {
public static final Vector/*<RemoteDevice>*/ devicesDiscovered = new Vector();
public static void main() throws IOException, InterruptedException {
final Object inquiryCompletedEvent = new Object();
devicesDiscovered.clear();
final DiscoveryListener listener = new DiscoveryListener() {
public void deviceDiscovered(RemoteDevice btDevice, DeviceClass cod) {
devicesDiscovered.addElement(btDevice);
//
String testingAgain = devicesDiscovered.toString();
System.out.println("What?? : " + testingAgain);
/*
* As far as i know, the following two FOR loops do the same thing
* But both of them are not being executed...
*/
//Its not executing this...
for(int i=0; i< devicesDiscovered.size(); i++) {
System.out.println("test if this gets output");
String test = (String) devicesDiscovered.elementAt(i);
System.out.println("Test: " + test);
}
//Its not executing this....
for(int i=0; i> ((List) btDevice).size(); i++){
System.out.println("test if this gets output 1");
String testing = (String) devicesDiscovered.toString();
System.out.print("Test1: " + testing);
}
//Prints the MAC addresses [macaddress, macaddress, macaddress, etc]
// System.out.println(devicesDiscovered);
/*
* Now need to extract each macaddress from devicesDiscovered
* and convert from a Vector to a String
*/
}
public void inquiryCompleted(int discType) {
System.out.println("Device Inquiry completed!");
synchronized(inquiryCompletedEvent){
inquiryCompletedEvent.notifyAll();
}
}
public void serviceSearchCompleted(int transID, int respCode) {
}
public void servicesDiscovered(int transID, ServiceRecord[] servRecord) {
}
};
synchronized(inquiryCompletedEvent) {
boolean started = LocalDevice.getLocalDevice().getDiscoveryAgent().startInquiry(DiscoveryAgent.GIAC, listener);
if (started) {
System.out.println("wait for device inquiry to complete...");
inquiryCompletedEvent.wait();
System.out.println(devicesDiscovered.size() + " device(s) found");
}
}
}
}
Can anyone spot any reason(s) as to why these two for loops are not working?
Thanks a lot
- Ryan
In this line
//Its not executing this....
for(int i=0; i > ((List) btDevice).size(); i++) {
You have turned the > the wrong way... try
for(int i=0; i < ((List) btDevice).size(); i++) {
instead.
(The reason it doesn't iterate, is because the initial value, 0, is not greater than the size of the list!)
In your first loop:
//Its not executing this...
for(int i=0; i< devicesDiscovered.size(); i++) {
System.out.println("test if this gets output");
it must be the case that devicesDiscovered is empty. I suggest you do
System.out.println(devicesDiscovered.size());
before the loop to debug.
The execution of your code in my machine is the following:
BlueCove version 2.1.0 on bluez
wait for device inquiry to complete...
What?? : [...]
test if this gets output
Test: ...
Device Inquiry completed!
1 device(s) found
BlueCove stack shutdown completed
With the following for loop:
for(int i=0; i< devicesDiscovered.size(); i++)
{
System.out.println("test if this gets output");
String test = (String) devicesDiscovered.elementAt(i).toString();
System.out.println("Test: " + test);
}
I've noticed that you were testing which one of the for loops was generating the output that you wanted. I can say that the above one works but the second generates an exception. You are trying to cast a RemoteDevice object to a List and iterate through it (for(int i=0; i < ((List) btDevice).size(); i++)). That's the reason for not working and therefore the exception.

Resources