I am working on a quiz app. I have created a lot of questions and saved them as CSV files. My script fails when it gets to the line of taking the data from the CSV and entering the data into an array. My code is shown below. If I remove lines for the array answers[] then the code works correctly and creates 150 questions with no data for the answers[] array and correct data in all other fields. What am I doing wrong with the array?
public static void GenerateQuestions() //edit function name
{
string[] allLines = File.ReadAllLines(Application.dataPath + questionCSVPath); //edit variable name
foreach(string s in allLines)
{
string[] splitData = s.Split(',');
QuestionCSV question = ScriptableObject.CreateInstance<QuestionCSV>(); //no change to class unless different type of setup such as 2 or 3 stacked numbers
question.ID = splitData[0];
question.question = splitData[1];
question.answers[0] = splitData[2];
question.answers[1] = splitData[3];
question.answers[2] = splitData[4];
question.answers[3] = splitData[5];
question.correctAnswer = splitData[6];
question.solution = splitData[7];
AssetDatabase.CreateAsset(question, $"Assets/Resources/Questions/4thSub3/{question.ID}.asset");
}
AssetDatabase.SaveAssets();
}
The QuestionCSV script is:
public class QuestionCSV : ScriptableObject {
[SerializeField]
private string ID;
[SerializeField]
private string question;
[SerializeField]
private string[] answers;
[SerializeField]
private int correctAnswer;
[SerializeField]
private string solution;
What am I doing wrong with the array?
Related
i am a total beginner with unity and have very little to no knowledge in programming.
for an unity game scene i would need to pull random texts that should be displayed in a textmesh canvas.
could somebody help me with a beginner friendly how-to do this?
i found this posting that discribes some code how to do it...
edited (this is the correct link):
https://answers.unity.com/questions/756090/how-can-i-change-textmesh-to-a-random-text-snippet.html
But how can this code be implemented.
I would create a new cs-script file. What should i include besides of the code mentioned? And how can i connect that to the textmesh object in the canvas of the game scene?
Hope you can help me and sorry for asking such stupid/basic things!
All the best,
Mark
I found the best way to do this would be to have a string array:
string[] words = { "word A", "word B" }; etc...
then you can call this line of code which will have your random word
string randomWord = words[Random.Range(0, words.Length)];
ok, how can this be used?
here is a way:
using UnityEngine;
using TMPro;
public class something : MonoBehaviour
{
public string[] words = { "Word A", "Word B" };
public TMP_Text text;
private void Start()
{
// log whatever comes out of the RandomWord string.
string wordToDisplay = RandomWord();
text.text = wordToDislpay;
}
// when you see a string function,
// it will return a string that
// you can use anywhere!
private string RandomWord()
{
// grab a random string from the words array
string randomWord = words[Random.Range(0, randomWord.Length)];
// return it (this will be the string that the script will use)
return randomWord;
}
}
if you are using the normal UI text and not TEXT MESH PRO, then instead of using TMPro;, use using UnityEngine.UI;, And TMP_Text would just be Text. the string array can be changed in the inspector, which is the best bit!
You should be able to do anything you want with this.
Good luck with unity!
i was able to solve it using this code.
big thanks to you!!!!
using UnityEngine;
using TMPro;
public class RandomText1 : MonoBehaviour
{
public string[] words = { "XA", "XB", "XC" };
public TMP_Text text;
private void Start()
{
// log whatever comes out of the RandomWord string.
string wordToDisplay = RandomWord();
text.text = wordToDisplay;
}
// when you see a string function,
// it will return a string that
// you can use anywhere!
private string RandomWord()
{
// grab a random string from the words array
string randomWord = words[Random.Range(0, words.Length)];
// return it (this will be the string that the script will use)
return randomWord;
}
}
What it looks like in unity. I have also drawn a red arrow to show what I am trying to move
I am trying to make a quiz game that would show a random word with a corresponding image. I am able to show a random question and a random image at the same time. My problem is that the image that will show up will not always be the correct image for the word. Is it possible to move the random image up and place it with the random text?
I have tried attaching the image directly with the text and the problem is that the text will disappear and only the image will stay. What I did to get the image into the game object was by repeating everything for the text.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
public class GameManager : MonoBehaviour
{
public Question[] questions;
public Image[] images;
private static List<Question> unansweredQuestions;
private static List<Image> unansweredImages;
private Question currentQuestion;
private Image currentImage;
[SerializeField]
private Text factText;
[SerializeField]
private Image factImage;
[SerializeField]
private Text trueAnswerText;
[SerializeField]
private Image trueAnswerImage;
[SerializeField]
private Text falseAnswerText;
[SerializeField]
private Image falseAnswerImage;
[SerializeField]
private Animator animator;
[SerializeField]
private float timeBetweenQuestions = 1f;
[SerializeField]
private float timeBetweenImages = 1f;
void Start()
{
if (unansweredQuestions == null || unansweredQuestions.Count == 0)
{
unansweredQuestions = questions.ToList<Question>();
}
if (unansweredImages == null || unansweredImages.Count == 0)
{
unansweredImages = images.ToList<Image>();
}
SetCurrentQuestion();
}
void SetCurrentQuestion()
{
int randomQuestionIndex = Random.Range(0, unansweredQuestions.Count);
currentQuestion = unansweredQuestions[randomQuestionIndex];
int randomImageIndex = Random.Range(0, unansweredQuestions.Count);
currentImage = unansweredImages[randomImageIndex];
factText.text = currentQuestion.fact;
if (currentQuestion.isTrue)
{
trueAnswerText.text = "CORRECT";
falseAnswerText.text = "WRONG";
}
else
{
trueAnswerText.text = "WRONG";
falseAnswerText.text = "CORRECT";
}
}
IEnumerator TransitionToNextQuestion()
{
unansweredQuestions.Remove(currentQuestion);
yield return new WaitForSeconds(timeBetweenQuestions);
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
unansweredImages.Remove(currentImage);
yield return new WaitForSeconds(timeBetweenQuestions);
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);
}
I expect the image and correct text to show up together randomly.
Based on the small information I think you are trying to set a random question but with the correct image.
If this is the case:
The problem I see is you are using a list to access Question and Images. You can use Dictionaries to solve the issue.
public Question[] questions;
public Image[] images;
New:
Dictionary< int, Question> Questions;
Dictionary< int, Image> Images;
Integer here will be a random index that will be same for both dictionaries.
You can manually set the questions and images on runtime. then, whenever you want to assign an image to that random text, just get a random number using Random.Range function. it will return an integer that can be used as the index. Let me know if it helps.
PS: You can remove the space before int datatype.
I am trying to automatically list all declared variables on my class. I am working on a pooling script, so for each gameobject I have a prefab, an array, an array size and a pool size. (they all follow naming standards, prefab name + "array" for array and etc).
private GameObject _PlayerLaserPrefab;
private GameObject[] _PlayerLaserPrefabArray;
private int _PlayerLaserPrefabPoolsize = 30;
private Queue<Transform> _PlayerLaserPrefabQueue = new Queue<Transform>();
private void Initiate_PlayerLaserPrefabPool()
{
_PlayerLaserPrefabArray = new GameObject[_PlayerLaserPrefabPoolsize];
for (int i = 0; i < _PlayerLaserPrefabPoolsize; i++)
{
_
PlayerLaserPrefabArray[i] = Instantiate(_PlayerLaserPrefab,Vector3.zero,
Quaternion.identity) as GameObject;
Transform Obj=PlayerLaserPrefabArray[i].GetComponent<Transform>();
ObjTransform1.parent = transform;
_PlayerLaserPrefabQueue.Enqueue(ObjTransform1);
_PlayerLaserPrefabArray[i].SetActive(false);
}
}
this code has to be repeated for all game´s objects so I am looking for a method to dynamically generate these funcions and variables based on the object´s name.
//pseudo code would be:
foreach (gameobject in list)
create array
create queue
initiate pool
Generate spawn function
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AnimationCamera : MonoBehaviour
{
public Camera animationCamera;
public Camera mainCamera;
Animator _anim;
List<string> animations = new List<string>();
private void Start()
{
animationCamera.enabled = false;
mainCamera.enabled = true;
_anim = GetComponent<Animator>();
foreach (AnimationClip ac in _anim.runtimeAnimatorController.animationClips)
{
animations.Add(ac.name + " " + ac.length.ToString());
}
int cliptoplay = animations[0].IndexOf(" ");
string clip = animations[0].Substring(0, cliptoplay);
}
In the end in the variable string clip i'm getting the name.
And in the List animations i have the length of each clip.
But i wonder if i could do something like that if i will type in visual studio in the code only: clip.
And after the point(clip.) i will have a list of options of each clip name and it's length. For example if i type today animations. i get a list of properties like: animations.Add or animations.Insert or animations.IndexOf
What i want to do is to create some so if i will type Clip. i will get a list of all clips names and the length for example: Clip.anim_001_length_10 or Clip.myanim_length_21
So if i want to use it later it will be easier to find the clip you want to use.
Hopefully I understood you correctly, but why not just use a list of AnimationClip instead of doing the string manipulation?
List<AnimationClip> animations = new List<AnimationClip>();
Then later you can populate it by creating new AnimationClip objects and copying the properties from the controller's collection:
foreach (AnimationClip ac in _anim.runtimeAnimatorController.animationClips)
{
animations.Add(new AnimationClip {name = ac.name, length = ac.length});
}
Now if you want to get a list of all clip names, you can do something like:
List<string> clipNames = animations.Select(clip => clip.name).ToList();
Or if you want all clips with a length < 30:
List<AnimationClip> shortClips = animations.Where(clip => clip.length < 30).ToList();
I am trying to make an easy to use button inside the Unity Editor for Character and Item creation.
I will throw out a little extra info here to help explain my issue.
My game is structured like this;
Game Controller >> Character Script >> (PlayerName)Script
A character object has both the character script and a script named after it, on it.
I want to be able to click "Create New Character" in the Unity editor and it do the following;
1) Prompt for a Name to use.
2) Create Empty Game Object named Name from whatever the user typed in.
3) Create a new C# Script named the same, and add it to the object.
-I want the generated script to have some pre-determined "Character Template" code in it.
4) Attach the new Script to the new empty game object, and attach a "Character Script" to it as well.
Thanks in advance.
One last sub-question.
Would it be better to Access the PlayerNamedScript from the GameController by a public monobehaviour on the Character Script?
Or can the CharacterScript Dynamically extend the PlayerNamedScript, sibling.
I hope that is clear. Thanks again.
Try this out
Put the CharacterCreatorEditor.cs in a folder named Editor somewhere in your project.
CharacterCreatorEditor.cs
using UnityEngine;
using System.Collections;
using UnityEditor;
using System.IO;
using System.Text.RegularExpressions;
public class CharacterCreatorEditor : EditorWindow {
#region Character Fields
//Add as many character specific fields / variables you want here.
//Remember to update the same thing in the "CharacterTemplate.txt"!
public string characterName = "John Doe";
public float characterHealth = 10;
public int characterCost = 1000;
public bool isBadGuy = false;
#endregion
private bool needToAttach = false; //A boolean that checks whether a newly created script has to be attached
private float waitForCompile = 1; //Counter for compile
GameObject tempCharacter; //A temporary GameObject that we assign the new chracter to.
//A Menu Item when clicked will bring up the Editor Window
[MenuItem ("AxS/Create New Character")]
public static void CreateNewChar () {
EditorWindow.GetWindow(typeof(CharacterCreatorEditor));
}
void OnGUI () {
GUILayout.Label("Here's a sample Editor Window. Put in more variables as you need below.");
GUILayout.Space(10);
//Note on adding more fields
//The code below is broken into groups, one group per variable
//While it's relatively long, it keeps the Editor Window clean
//Most of the code should be fairly obvious
GUILayout.BeginHorizontal();
GUILayout.Label("Character Name", new GUILayoutOption[0]);
characterName = EditorGUILayout.TextField(characterName, new GUILayoutOption[0]);
GUILayout.EndHorizontal();
GUILayout.Space(10);
GUILayout.BeginHorizontal();
GUILayout.Label("Character Health", new GUILayoutOption[0]);
characterHealth = EditorGUILayout.FloatField(characterHealth, new GUILayoutOption[0]);
GUILayout.EndHorizontal();
GUILayout.Space(10);
GUILayout.BeginHorizontal();
GUILayout.Label("Character Cost", new GUILayoutOption[0]);
characterCost = EditorGUILayout.IntField(characterCost, new GUILayoutOption[0]);
GUILayout.EndHorizontal();
GUILayout.Space(10);
GUILayout.BeginHorizontal();
GUILayout.Label(string.Format("Is {0} a Bad Guy?", new object[] { characterName }), new GUILayoutOption[0]);
isBadGuy = EditorGUILayout.Toggle(isBadGuy, new GUILayoutOption[0]);
GUILayout.EndHorizontal();
GUILayout.Space(10);
GUI.color = Color.green;
//If we click on the "Done!" button, let's create a new character
if(GUILayout.Button("Done!", new GUILayoutOption[0]))
CreateANewCharacter();
}
void Update () {
//We created a new script below (See the last few lines of CreateANewCharacter() )
if(needToAttach) {
//Some counter we just keep reducing, so we can give the
//EditorApplication.isCompiling to kick in
waitForCompile -= 0.01f;
//So a few frames later, we can assume that the Editor has enough
//time to "catch up" and EditorApplication.isCompiling will now be true
//so, we wait for the newly created script to compile
if(waitForCompile <= 0) {
//The newly created script is done compiling
if(!EditorApplication.isCompiling) {
//Lets add the script
//Here we add the script using the name as a string rather than
//it's type in Angled braces (As done below)
tempCharacter.AddComponent(characterName.Replace(" ", ""));
//Reset the control variables for attaching these scripts.
needToAttach = false;
waitForCompile = 1;
}
}
}
}
private void CreateANewCharacter () {
//Instantiate a new GameObject
tempCharacter = new GameObject();
//Name it the same as the Character Name
tempCharacter.name = characterName;
//Add the ChracterScript component. Note the use of angle braces over quotes
tempCharacter.AddComponent<CharacterScript>();
//Loading the template text file which has some code already in it.
//Note that the text file is stored in the path PROJECT_NAME/Assets/CharacterTemplate.txt
TextAsset templateTextFile = AssetDatabase.LoadAssetAtPath("Assets/CharacterTemplate.txt",
typeof(TextAsset)) as TextAsset;
string contents = "";
//If the text file is available, lets get the text in it
//And start replacing the place holder data in it with the
//options we created in the editor window
if(templateTextFile != null) {
contents = templateTextFile.text;
contents = contents.Replace("CHARACTERCLASS_NAME_HERE", characterName.Replace(" ", ""));
contents = contents.Replace("CHARACTER_NAME_HERE", characterName);
contents = contents.Replace("CHARACTER_HEALTH_HERE", characterHealth.ToString());
contents = contents.Replace("CHARACTER_COST_HERE", characterCost.ToString());
contents = contents.Replace("CHARACTER_BAD_GUY_HERE", isBadGuy.ToString().ToLower());
}
else {
Debug.LogError("Can't find the CharacterTemplate.txt file! Is it at the path YOUR_PROJECT/Assets/CharacterTemplate.txt?");
}
//Let's create a new Script named "CHARACTERNAME.cs"
using(StreamWriter sw = new StreamWriter(string.Format(Application.dataPath + "/{0}.cs",
new object[] { characterName.Replace(" ", "") }))) {
sw.Write(contents);
}
//Refresh the Asset Database
AssetDatabase.Refresh();
//Now we need to attach the newly created script
//We can use EditorApplication.isCompiling, but it doesn't seem to kick in
//after a few frames after creating the script. So, I've created a roundabout way
//to do so. Please see the Update function
needToAttach = true;
}
}
Put the below text file into the path "YOUR_PROJECT/Assets/CharacterTemplate.txt" If you don't, the code WON'T WORK!
CharacterTemplate.txt
using UnityEngine;
using System.Collections;
public class CHARACTERCLASS_NAME_HERE : MonoBehaviour {
public string characterName = "CHARACTER_NAME_HERE";
public float characterHealth = CHARACTER_HEALTH_HERE;
public int characterCost = CHARACTER_COST_HERE;
public bool isBadGuy = CHARACTER_BAD_GUY_HERE;
public void SomeMethod () {
}
}
Explanation of the code
First, the editor script takes all the input variables (should be fairly obvious what they are)
Once you click the done button, the following happen
A new GameObject is Instantiated
The instantiated GameObject is named the same as the Character Name in the Editor (eg. John Doe)
The CharacterScript (your common script) is attached
The template text file ("CharacterTemplate.txt") is read, and all the data is replaced with the data you entered in the Editor Window
This is then written to a new script file
We refresh the Asset Database, and wait until the newly created script is compiled (eg. JohnDoe.cs)
Lastly attach the script to the GameObject instantiated in Step 1
For your second question, what you'll need to do is to have all your PlayerNamedClass extend the same base class. This way, you can type the variable you'll expose in CharacterScript
So, for example, if you call the base class "NamedCharacterScripts"
In JohnDoe.cs
public class JohnDoe : NamedCharacterScripts
In JaneDoe.cs
public class JaneDoe : NamedCharacterScripts
In CharacterScript.cs
public NamedCharacterScripts namedCharacterScript;
void Awake () {
//This will assign JohnDoe.cs for the GameObject named "John Doe" &
//JaneDoe.cs to the GameObject named "Jane Doe"
namedCharacterScript = GetComponent<NamedCharacterScripts>();
}
Hope this answers your questions. If you have trouble, just leave a comment
My script is not production-ready like Venkat's answer, but it should be easier to understand for educational purposes.
using UnityEngine;
using System.Collections;
using UnityEditor;
using System.IO;
[ExecuteInEditMode]
public class CharacterTools : MonoBehaviour
{
[SerializeField, HideInInspector]
private string className;
private bool waitForCompile = false;
private void Update()
{
if (string.IsNullOrEmpty(className))
return;
if (waitForCompile && EditorApplication.isCompiling)
waitForCompile = false;
if (!waitForCompile && !EditorApplication.isCompiling)
{
var gameObject = new GameObject(className);
Debug.Log("Attempting to add " + className);
var c = gameObject.AddComponent(className);
className = null;
}
}
[ContextMenu("Create character")]
private void CreateCharacter()
{
string name = "Number" + Random.Range(0, 100).ToString();
string nameTemplate = "{0}Character";
string contentTemplate = #"using UnityEngine;
public class {0} : MonoBehaviour
{{
}}
";
var className = string.Format(nameTemplate, name);
var path = Application.dataPath + "/" + className + ".cs";
var scriptFile = new StreamWriter(path);
scriptFile.Write(string.Format(contentTemplate, className));
scriptFile.Close();
AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceSynchronousImport);
AssetDatabase.Refresh();
this.className = className;
this.waitForCompile = true;
}
}
Usage:
Add this script to any object on scene.
Right click on just added component in inspector.
Choose “Create character”.
Wait few seconds.