I am a beginner in Unity and I am currently making a simple game. I have a problem managing the flow of my minigame. The minigame is simply finding an object, when I found and tap on the item name below will be shaded or marked or there can be animation just to indicate that the item is found.
What I want to do is to get the name of the objects that need to be found and set them randomly in the three (3) item names below. like every time this minigame opens the names of the items are randomly placed in the 3 texts. And when the item is found the name below will be marked or shaded or anything that will indicate it is found, but for now I will just set it inactive for easier indication. How can I properly do this whole process?
The objects inside the scene are button for them to have onCLick() events
Correction: the term choices are wrong because they just display the name of the items that you need to find, just in case you get confused with the term choice in my minigame. I will fix it.
Here is the visuals for the minigame:
The script I currently have for when the objects was clicked:
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using System.Collections.Generic;
using TMPro;
public class ClickObject : MonoBehaviour
{
[SerializeField] Button pillowBtn, pcBtn, lampBtn;
// [SerializeField] TextMeshProUGUI choice1, choice2, choice3;
// FROM THE SOLUTION
[SerializeField] List<GameObject> gameObjectWanted;
[SerializeField] List<TextMeshProUGUI> textBoxes;
// METHOD NAMES IS TEMPORARY
public void pillowClicked()
{
Debug.Log("you found the " + EventSystem.current.currentSelectedGameObject.name);
}
public void desktopClicked()
{
Debug.Log("you found the " + EventSystem.current.currentSelectedGameObject.name);
}
public void lampClicked()
{
Debug.Log("you found the " + EventSystem.current.currentSelectedGameObject.name);
}
}
You asked for a lot and I hope I understood your intention.
first, if you want to randomly choose an amount of game objects from your game, I think the best way to do it is by adding the refernece of all the game objects you want to choose from radomly inside a list of game objects and then randomly take a game object of the list and make it a child of another game object I call "fatherGoTranform" on my code like that:
[SerializeField] List<GameObject> gameObjectWanted;
[SerializeField] float numOfGO = 4;
[SerializeField] Transform fatherGoTranform;
void Start()
{
for(int i=0;i<numOfGO;i++)
{
int index = Random.Range(0, gameObjectWanted.Count-1);
GameObject currentGO = gameObjectWanted[index ];
currentGO.transform.parent = fatherGoTranform;
gameObjectWanted.RemoveAt(index);
}
}
and then to click on a game object and the do with what you want try this:
void Update()
{
//Check for mouse click
if (Input.GetMouseButtonDown(0))
{
RaycastHit raycastHit;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out raycastHit, 100f))
{
if (raycastHit.transform != null)
{
//Our custom method.
CurrentClickedGameObject(raycastHit.transform.gameObject);
}
}
}
}
I have not checked the code so if there is an error tell me and I will fix it
This is the solution that worked for my problem. I randomly placed numbers to the list according to childCount and every index indicate the index of the text that I want to put them on which I get as a Transform child.
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using System.Collections.Generic;
using TMPro;
public class ClickObject : MonoBehaviour
{
// [SerializeField] Button pillowBtn, pcBtn, lampBtn;
[SerializeField] GameObject choices, v1, finishPanel;
// RANDOM NUMBER HOLDER FOR CHECKING
private int randomNumber, foundCount;
// RANDOMIZED NUMBER LIST HOLDER
public List<int> RndmList = new List<int>();
private void Awake()
{
foundCount = 0;
RndmList = new List<int>(new int[v1.transform.childCount]);
for (int i = 0; i < v1.transform.childCount; i++)
{
randomNumber = UnityEngine.Random.Range(0, (v1.transform.childCount) + 1);
while (RndmList.Contains(randomNumber))
{
randomNumber = UnityEngine.Random.Range(0, (v1.transform.childCount) + 1);
}
RndmList[i] = randomNumber;
// Debug.Log(v1.transform.GetChild(randomNumber-1).name);
choices.transform.GetChild(i).GetComponentInChildren<TextMeshProUGUI>().text = v1.transform.GetChild(randomNumber - 1).name;
}
}
public void objectFound()
{
string objectName = EventSystem.current.currentSelectedGameObject.name;
Debug.Log("you found the " + objectName);
for (int i = 0; i < choices.transform.childCount; i++)
{
if (objectName == choices.transform.GetChild(i).GetComponentInChildren<TextMeshProUGUI>().text)
{
// Debug.Log(i);
// choices.transform.GetChild(i).gameObject.SetActive(false);
// choices.transform.GetChild(i).GetComponentInChildren<TextMeshProUGUI>().color = Color.gray;
if(RndmList.Contains(i+1))
{
// Debug.Log(i);
// Debug.Log(v1.transform.GetChild(RndmList[i]-1).name);
choices.transform.GetChild(i).GetComponentInChildren<TextMeshProUGUI>().color = Color.gray;
v1.transform.GetChild(RndmList[i]-1).GetComponent<Button>().enabled = false;
foundCount++;
}
}
}
if(foundCount == v1.transform.childCount)
{
finishPanel.SetActive(true);
}
}
}
I need to get the object in an observable collection by index to access a property of the item at that index.
This is a snippet of the code:
public ObservableCollection<TipsModel> TipObjects;
private void LoadContent()
{
TipObjects = new ObservableCollection<TipsModel>();
for (int i = 0; i < 5; i++)
{
TipsModel item = new TipsModel()
{
Image = ImageSource.FromFile("nonindustryIcon.png"),
Title = "Kill energy vampires and save up to $100 a year",
Text = "Seventy-five percentof the electrical use by home electronics occurs when they're at home. \n People not at home means no electricity. Do not stay at home. Go stay on the streets. ",
};
TipObjects.Add(item);
}
foreach (TipsModel item in TipObjects)
{
img = item.Image;
tipTitle = item.Title;
tip = item.Text;
item.Content = CreateContent();
}
slideView.ItemsSource = TipObjects;
}
private void slideView_SlidedToIndex(object sender, Telerik.XamarinForms.Primitives.SlideView.SlideViewSlidedToIndexEventArgs e)
{
var slideId = slideView.Id;
//TipsModel tip = TipObjects.item at index[18];
}
You can just normally do
var tip = TipObjects[18];
Observable collection is just a normal, fully functional collection that supports indexing.
Alternatively you can use the Items property as well. Both approaches are equivalent:
var tip = TipObjects.Items[18];
I am creating a program that prompts for lastname, age, and weight. It then displays that info, then asks again. Typing "finished" will make it stop asking and it will display all of the information for each person.
The class "Person" has lastName as String, age and weight as double.
I am stuck on how to create new instances of a person in the for loop and then print out each of those instances once the loop is done.
public static void main(String[] args) {
Scanner userInput = new Scanner(System.in);
String decision;
Person Guy = null;
for (int i =0; i<20; i++){
Guy = new Person();
System.out.println("Please enter a last name");
Guy.lastName = userInput.next();
if (Guy.lastName.equalsIgnoreCase("finished")){
break;
}
System.out.println("Please enter an age");
Guy.age = userInput.nextDouble();
System.out.println("Please enter a weight");
Guy.weight = userInput.nextDouble();
Guy.WriteOutput();
}
for (int i=0; i<20; i++){
Guy.WriteOutput();
if (Guy == null){
break;
}
}
}
}
You're dropping the old guy on the floor with every iteration. Instead you want to add each guy to a structure (e.g. an ArrayList), and after the input phase is done then iterate over that list and print out each guy.
I'm using the Stanford Named Entity Recognizer http://nlp.stanford.edu/software/CRF-NER.shtml and it's working fine. This is
List<List<CoreLabel>> out = classifier.classify(text);
for (List<CoreLabel> sentence : out) {
for (CoreLabel word : sentence) {
if (!StringUtils.equals(word.get(AnswerAnnotation.class), "O")) {
namedEntities.add(word.word().trim());
}
}
}
However the problem I'm finding is identifying names and surnames. If the recognizer encounters "Joe Smith", it is returning "Joe" and "Smith" separately. I'd really like it to return "Joe Smith" as one term.
Could this be achieved through the recognizer maybe through a configuration? I didn't find anything in the javadoc till now.
Thanks!
This is because your inner for loop is iterating over individual tokens (words) and adding them separately. You need to change things to add whole names at once.
One way is to replace the inner for loop with a regular for loop with a while loop inside it which takes adjacent non-O things of the same class and adds them as a single entity.*
Another way would be to use the CRFClassifier method call:
List<Triple<String,Integer,Integer>> classifyToCharacterOffsets(String sentences)
which will give you whole entities, which you can extract the String form of by using substring on the original input.
*The models that we distribute use a simple raw IO label scheme, where things are labeled PERSON or LOCATION, and the appropriate thing to do is simply to coalesce adjacent tokens with the same label. Many NER systems use more complex labels such as IOB labels, where codes like B-PERS indicates where a person entity starts. The CRFClassifier class and feature factories support such labels, but they're not used in the models we currently distribute (as of 2012).
The counterpart of the classifyToCharacterOffsets method is that (AFAIK) you can't access the label of the entities.
As proposed by Christopher, here is an example of a loop which assembles "adjacent non-O things". This example also counts the number of occurrences.
public HashMap<String, HashMap<String, Integer>> extractEntities(String text){
HashMap<String, HashMap<String, Integer>> entities =
new HashMap<String, HashMap<String, Integer>>();
for (List<CoreLabel> lcl : classifier.classify(text)) {
Iterator<CoreLabel> iterator = lcl.iterator();
if (!iterator.hasNext())
continue;
CoreLabel cl = iterator.next();
while (iterator.hasNext()) {
String answer =
cl.getString(CoreAnnotations.AnswerAnnotation.class);
if (answer.equals("O")) {
cl = iterator.next();
continue;
}
if (!entities.containsKey(answer))
entities.put(answer, new HashMap<String, Integer>());
String value = cl.getString(CoreAnnotations.ValueAnnotation.class);
while (iterator.hasNext()) {
cl = iterator.next();
if (answer.equals(
cl.getString(CoreAnnotations.AnswerAnnotation.class)))
value = value + " " +
cl.getString(CoreAnnotations.ValueAnnotation.class);
else {
if (!entities.get(answer).containsKey(value))
entities.get(answer).put(value, 0);
entities.get(answer).put(value,
entities.get(answer).get(value) + 1);
break;
}
}
if (!iterator.hasNext())
break;
}
}
return entities;
}
I had the same problem, so I looked it up, too. The method proposed by Christopher Manning is efficient, but the delicate point is to know how to decide which kind of separator is appropriate. One could say only a space should be allowed, e.g. "John Zorn" >> one entity. However, I may find the form "J.Zorn", so I should also allow certain punctuation marks. But what about "Jack, James and Joe" ? I might get 2 entities instead of 3 ("Jack James" and "Joe").
By digging a bit in the Stanford NER classes, I actually found a proper implementation of this idea. They use it to export entities under the form of single String objects. For instance, in the method PlainTextDocumentReaderAndWriter.printAnswersTokenizedInlineXML, we have:
private void printAnswersInlineXML(List<IN> doc, PrintWriter out) {
final String background = flags.backgroundSymbol;
String prevTag = background;
for (Iterator<IN> wordIter = doc.iterator(); wordIter.hasNext();) {
IN wi = wordIter.next();
String tag = StringUtils.getNotNullString(wi.get(AnswerAnnotation.class));
String before = StringUtils.getNotNullString(wi.get(BeforeAnnotation.class));
String current = StringUtils.getNotNullString(wi.get(CoreAnnotations.OriginalTextAnnotation.class));
if (!tag.equals(prevTag)) {
if (!prevTag.equals(background) && !tag.equals(background)) {
out.print("</");
out.print(prevTag);
out.print('>');
out.print(before);
out.print('<');
out.print(tag);
out.print('>');
} else if (!prevTag.equals(background)) {
out.print("</");
out.print(prevTag);
out.print('>');
out.print(before);
} else if (!tag.equals(background)) {
out.print(before);
out.print('<');
out.print(tag);
out.print('>');
}
} else {
out.print(before);
}
out.print(current);
String afterWS = StringUtils.getNotNullString(wi.get(AfterAnnotation.class));
if (!tag.equals(background) && !wordIter.hasNext()) {
out.print("</");
out.print(tag);
out.print('>');
prevTag = background;
} else {
prevTag = tag;
}
out.print(afterWS);
}
}
They iterate over each word, checking if it has the same class (answer) than the previous, as explained before. For this, they take advantage of the fact expressions considered as not being entities are flagged using the so-called backgroundSymbol (class "O"). They also use the property BeforeAnnotation, which represents the string separating the current word from the previous one. This last point allows solving the problem I initially raised, regarding the choice of an appropriate separator.
Code for the above:
<List> result = classifier.classifyToCharacterOffsets(text);
for (Triple<String, Integer, Integer> triple : result)
{
System.out.println(triple.first + " : " + text.substring(triple.second, triple.third));
}
List<List<CoreLabel>> out = classifier.classify(text);
for (List<CoreLabel> sentence : out) {
String s = "";
String prevLabel = null;
for (CoreLabel word : sentence) {
if(prevLabel == null || prevLabel.equals(word.get(CoreAnnotations.AnswerAnnotation.class)) ) {
s = s + " " + word;
prevLabel = word.get(CoreAnnotations.AnswerAnnotation.class);
}
else {
if(!prevLabel.equals("O"))
System.out.println(s.trim() + '/' + prevLabel + ' ');
s = " " + word;
prevLabel = word.get(CoreAnnotations.AnswerAnnotation.class);
}
}
if(!prevLabel.equals("O"))
System.out.println(s + '/' + prevLabel + ' ');
}
I just wrote a small logic and it's working fine. what I did is group words with same label if they are adjacent.
Make use of the classifiers already provided to you. I believe this is what you are looking for:
private static String combineNERSequence(String text) {
String serializedClassifier = "edu/stanford/nlp/models/ner/english.all.3class.distsim.crf.ser.gz";
AbstractSequenceClassifier<CoreLabel> classifier = null;
try {
classifier = CRFClassifier
.getClassifier(serializedClassifier);
} catch (ClassCastException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(classifier.classifyWithInlineXML(text));
// FOR TSV FORMAT //
//System.out.print(classifier.classifyToString(text, "tsv", false));
return classifier.classifyWithInlineXML(text);
}
Here is my full code, I use Stanford core NLP and write algorithm to concatenate Multi Term names.
import edu.stanford.nlp.ling.CoreAnnotations;
import edu.stanford.nlp.ling.CoreLabel;
import edu.stanford.nlp.pipeline.Annotation;
import edu.stanford.nlp.pipeline.StanfordCoreNLP;
import edu.stanford.nlp.util.CoreMap;
import org.apache.log4j.Logger;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
/**
* Created by Chanuka on 8/28/14 AD.
*/
public class FindNameEntityTypeExecutor {
private static Logger logger = Logger.getLogger(FindNameEntityTypeExecutor.class);
private StanfordCoreNLP pipeline;
public FindNameEntityTypeExecutor() {
logger.info("Initializing Annotator pipeline ...");
Properties props = new Properties();
props.setProperty("annotators", "tokenize, ssplit, pos, lemma, ner");
pipeline = new StanfordCoreNLP(props);
logger.info("Annotator pipeline initialized");
}
List<String> findNameEntityType(String text, String entity) {
logger.info("Finding entity type matches in the " + text + " for entity type, " + entity);
// create an empty Annotation just with the given text
Annotation document = new Annotation(text);
// run all Annotators on this text
pipeline.annotate(document);
List<CoreMap> sentences = document.get(CoreAnnotations.SentencesAnnotation.class);
List<String> matches = new ArrayList<String>();
for (CoreMap sentence : sentences) {
int previousCount = 0;
int count = 0;
// traversing the words in the current sentence
// a CoreLabel is a CoreMap with additional token-specific methods
for (CoreLabel token : sentence.get(CoreAnnotations.TokensAnnotation.class)) {
String word = token.get(CoreAnnotations.TextAnnotation.class);
int previousWordIndex;
if (entity.equals(token.get(CoreAnnotations.NamedEntityTagAnnotation.class))) {
count++;
if (previousCount != 0 && (previousCount + 1) == count) {
previousWordIndex = matches.size() - 1;
String previousWord = matches.get(previousWordIndex);
matches.remove(previousWordIndex);
previousWord = previousWord.concat(" " + word);
matches.add(previousWordIndex, previousWord);
} else {
matches.add(word);
}
previousCount = count;
}
else
{
count=0;
previousCount=0;
}
}
}
return matches;
}
}
Another approach to deal with multi words entities.
This code combines multiple tokens together if they have the same annotation and go in a row.
Restriction:
If the same token has two different annotations, the last one will be saved.
private Document getEntities(String fullText) {
Document entitiesList = new Document();
NERClassifierCombiner nerCombClassifier = loadNERClassifiers();
if (nerCombClassifier != null) {
List<List<CoreLabel>> results = nerCombClassifier.classify(fullText);
for (List<CoreLabel> coreLabels : results) {
String prevLabel = null;
String prevToken = null;
for (CoreLabel coreLabel : coreLabels) {
String word = coreLabel.word();
String annotation = coreLabel.get(CoreAnnotations.AnswerAnnotation.class);
if (!"O".equals(annotation)) {
if (prevLabel == null) {
prevLabel = annotation;
prevToken = word;
} else {
if (prevLabel.equals(annotation)) {
prevToken += " " + word;
} else {
prevLabel = annotation;
prevToken = word;
}
}
} else {
if (prevLabel != null) {
entitiesList.put(prevToken, prevLabel);
prevLabel = null;
}
}
}
}
}
return entitiesList;
}
Imports:
Document: org.bson.Document;
NERClassifierCombiner: edu.stanford.nlp.ie.NERClassifierCombiner;
So, I made a class that takes arrays and calculates a value from them. I then decided (unknowingly) to incorporate it into a GUI interface. All went well until I noticed this strange error; one of the jtextfields (prarie) would not store text while the other (yard) does.
I looked around and found my problem similiar to mine on this site;
Updating text in a JTextField
But he had one that doesn't work at all, where I have one that works and one that doesn't.
The Code is here (it's a bit long, but most of it is GUI), so hold your breath!:
import java.awt.GridLayout;
import javax.swing.Box;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
public class Window {
/**
* #param args
*/
private static int numb;
private static double micro, centi;
private static JTextField[] yard,prarie;
private static double[] charges,distances;
public static void main(String[] args)
{
//create a small dialog window to take in number of charged objects
JPanel startup = new JPanel();
JTextField many = new JTextField(5);
startup.add(many);
int result = JOptionPane.showConfirmDialog(null,startup , "Please Enter How Many Charged Objects are Being Evaluated", JOptionPane.OK_CANCEL_OPTION);
many.requestFocusInWindow();
//once ok is clicked, then the number input will be stored under 'numb'
//then proceed to inputFields
if (result == JOptionPane.OK_OPTION)
{
numb = Integer.parseInt(many.getText());
inputFields();
}
}
//this window opens the various JTextFields for input
public static void inputFields()
{
//top JTextFields
yard = new JTextField[numb];
JPanel chargePanel = new JPanel();
for(int x=0;x<numb;x++)
{
yard[x] =new JTextField(5);
chargePanel.add(new JLabel("Charge "+ Integer.toString(x+1)+":"));
chargePanel.add(yard[x]);
chargePanel.add(Box.createVerticalStrut(15)); // a spacer
}
//bottom JTextFields
prarie = new JTextField[numb-1];
JPanel meterPanel = new JPanel();
for(int x=0;x<numb-1;x++)
{
prarie[x]=new JTextField(5);
meterPanel.add(new JLabel("Meters "+ Integer.toString(x+1)+":"));
meterPanel.add(new JTextField(5));
meterPanel.add(Box.createVerticalStrut(15)); // a spacer
}
//JCheckBoxes
JCheckBox isMicro = new JCheckBox("Charges are in terms of microCoulombs");
JCheckBox isCm = new JCheckBox("Distances are in terms of centiMeters");
JPanel chechBox = new JPanel();
chechBox.add(isMicro);
chechBox.add(Box.createVerticalStrut(20));
chechBox.add(isCm);
//Paste them all together into one window
GridLayout gufi = new GridLayout(3,1);
JPanel host = new JPanel(gufi);
host.add(chargePanel);
host.add(meterPanel);
host.add(chechBox);
int result1 = JOptionPane.showConfirmDialog(null, host, "Please Enter Charge and Distance Values", JOptionPane.OK_CANCEL_OPTION);
//if ok is clicked, then go to 'printArr()' to print the JTextFields
//then go to assign the values from the JTextFields to private double arrays 'yard' and 'prarie'
if (result1 == JOptionPane.OK_OPTION)
{
micro = (isMicro.isSelected())? Math.pow(10, -6): 1;
centi = (isCm.isSelected())? .01: 1;
printArr();
assign();
}
}
//a makeshift method to print the value from the JTextFields
//to fix the problem of why prarie wouldn't store numbers
public static void printArr()
{
System.out.println("Charges are:");
for(int x=0;x<numb;x++)
System.out.print(yard[x].getText() + " ");
System.out.println("Distances are:");
for(int x=0;x<numb-1;x++)
System.out.print(prarie[x].getText() + " ");
}
//assigns values from JTextFields to the private double arrays 'yard' and 'prarie'
public static void assign()
{
try {
charges = new double[numb];
for(int x=0;x<numb;x++)
charges[x]=micro*Double.parseDouble(yard[x].getText().trim());
distances = new double[numb-1];
for(int x=0;x<numb-1;x++)
distances[x]=centi*Double.parseDouble(prarie[x].getText().trim());
} catch (NumberFormatException e) {
e.printStackTrace();
//inputFields();
}
calculate();
}
public static void calculate()
{
JPanel sample = new JPanel();
JTextField whichOne = new JTextField(5);
sample.add(whichOne);
int result = JOptionPane.showConfirmDialog(null,sample , "Please Enter Which Charged Object thy Wishs For", JOptionPane.OK_CANCEL_OPTION);
whichOne.requestFocusInWindow();
if (result == JOptionPane.OK_OPTION)
{
int target = Integer.parseInt(whichOne.getText());
}
}
}
Anyone who runs the code and takes the time to enter dummy values will see that 'yard' stores values while 'prarie' does not. Why is this?
*I'm pretty sure I'm overlooking obvious (as always).
Change:
meterPanel.add(new JTextField(5));
to:
meterPanel.add(prarie[x]);
in the for loop for the prarie textfields