Allowing User to Select multiple items in a GridView - arrays

Very new to coding through Android with no knowledge of Java watsoever. I've been putting together pieces through other questions posted but I'm struggling.
I created an array called 'images2' at the top of my OnCreate method and put a bunch of colors from my drawable into it, one by one. That part works great but I've been trying to create an onClickListner that has an OnClick method that changes the drawable when you click on it to another color. I want them all to change to the same color. I know that I can set the selector programmatically: gridview2.setSelector(new ColorDrawable(Color.BLACK)); but change the selector, either in the backend or XMl code doesn't help me get the new drawable to stick. So I created another listArray in the adapter and I can call it in the main. I have an index in the onClick that will go through all 25 possible item positions and I want to make it so that it will change the drawable to each position that is in the array. It also adds the current position clicked into the array myarray.add(position); but I just realized that value might not be being saved. The arrayList is declared public but it is declared public within the adapter so do changes in the OnCreate have no effect? I'm sorry. THAT is definitely a noobinoob-noob question.
/**
* Created by Jordan on 3/25/2017.
*/
public class Welcome extends AppCompatActivity implements AdapterView.OnItemClickListener {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.welcome);
final List<Integer> images2 = new ArrayList<Integer>();
images2.add(R.drawable.grid1);images2.add(R.drawable.grid6);
images2.add(R.drawable.grid2);images2.add(R.drawable.grid2);
images2.add(R.drawable.grid3);images2.add(R.drawable.grid1);
images2.add(R.drawable.grid4);images2.add(R.drawable.grid3);
images2.add(R.drawable.grid5);images2.add(R.drawable.grid2);
images2.add(R.drawable.grid6);images2.add(R.drawable.grid6);
images2.add(R.drawable.open);images2.add(R.drawable.grid4);
images2.add(R.drawable.grid1);images2.add(R.drawable.grid1);
images2.add(R.drawable.grid6);images2.add(R.drawable.grid4);
images2.add(R.drawable.grid4);images2.add(R.drawable.grid2);
images2.add(R.drawable.grid3);images2.add(R.drawable.grid3);
images2.add(R.drawable.grid6);images2.add(R.drawable.grid1);
images2.add(R.drawable.grid5);
this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
final GridView gridview2 = (GridView) findViewById(R.id.welcomeGrid);
final Welcome.ImageAdapter mAdapter = new Welcome.ImageAdapter(this, images2);
gridview2.setAdapter(mAdapter);
gridview2.setChoiceMode(GridView.CHOICE_MODE_MULTIPLE);
gridview2.setItemChecked(2, true);
gridview2.setOnItemClickListener(this);
gridview2.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
// ArrayList<Integer> clickedStatus = new ArrayList<Integer>();
ArrayList<Integer> clickedStatus = new ArrayList<>(25);
clickedStatus.add(position);
if (position == 12){
setContentView(R.layout.login);
}
else if(position != 12) {
// no matter, whether the item has already been clicked or not I want this toast to pop up to promp the user to actually enter the app and sign in
Toast.makeText(Welcome.this, "You selected " + position+ ".. Please select middle tile to enter!",
Toast.LENGTH_SHORT).show();
if(clickedStatus.contains(position)){
// as soon as it's noticed that the an integer matching the current position is currently stored in the
// array list (clickedStatus), I want it removed right below
// was passing item in place of position into the remove(). Don't know why Integer item = (Integer) parent.getItemAtPosition(position);
//I want to go through 25 numbers and
for(int i=0; i<25;i++ ){
// compare those ints to what is in contained in the clickedstatus
if(clickedStatus.contains(i)){
// Drawable Marker = getResources().getDrawable(R.drawable.celeb1);
images2.set(i, R.drawable.celeb1);
//for every int that is stored in click status I want
// to call the item and change the background of that item to a specified drawable file.
}
}
}
else {
clickedStatus.add(position);
// images2.set(position, );
for(int i=1; i<25;i++ ){
// compare those ints to what is in contained in the clickedstatus
if(clickedStatus.contains(i)){
// Drawable Marker = getResources().getDrawable(R.drawable.celeb1);
images2.set(i, R.drawable.celeb1);
//for every int that is stored in click status I want
// to call the item and change the background of that item to a specified drawable file.
}
}
}
// gridview2.setSelector(new ColorDrawable(Color.BLACK));
// mAdapter.notifyDataSetChanged();
}
}
}
);
}
// I think this got generated automatically at some point somehow. Might of been with the multichoicemode line above ^
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
}
public class ImageAdapter extends BaseAdapter {
private Context mContext;
//IMAGES ARE REFERENCED HERE V---
private List<Integer> mImages2;
public ArrayList<Integer> clickedStatus = new ArrayList<Integer>();
//create array to store whether an item has been clicked or not. needs to be public!!
//check to determine the case in the below method
// IGNORE THIS
// private int selectedPosition = -1;
//public void setSelectedPosition(int position) {
// selectedPosition = position;
//}
public ImageAdapter( final Context c, final List<Integer> images2){
mContext = c;
mImages2 = images2;
}
public int getCount() {
return mImages2.size();
}
public Object getItem(int position) {
return position;
}
public long getItemId(int position) {
return 1;
}
// create a new ImageView for each item referenced by the Adapter
public View getView(int position, View convertView, ViewGroup parent) {
ImageView imageView;
if (convertView == null) {
// if it's not recycled, initialize some attributes
imageView = new ImageView(mContext);
imageView.setLayoutParams(new GridView.LayoutParams(140, 120));
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setPadding(0, 0, 0, 0);
} else {
imageView = (ImageView) convertView;
}
if(clickedStatus.contains(position)){
convertView.setSelected(true);
convertView.setPressed(true);
convertView.setBackgroundColor(Color.GREEN);
}
imageView.setImageResource(mImages2.get(position));
return imageView;
}
;}
}

I created and finalized a different set of Arrays for holding every possible five combinations of selections that would result in a win. Titled Diagonal (1, 2, 3, 4, 5), Horizontal(1, 2, 3, 4, 5) , Vertical (1, 2, 3, 4, 5), etc. Upon click a value is stored at the appropriate position of the ListArray and I have relative checks (if statements) that loops everytime the button is click. Does not slow the app down substantially, looks ugly on paper but is very functional without the use of checkboxes. If anybody is interested in seeing the code they may comment.

Related

Out of range exception in Unity

I create a game in Unity. And I got this error:
System.ThrowHelper.ThrowArgumentOutOfRangeException (System.ExceptionArgument argument, System.ExceptionResource resource)
Here is my short code:
public class OnlineGame : MonoBehaviour
{
private List<GameObject> domino = new List<GameObject>(7);
public void Start()
{
StartGame();
}
private void StartGame()
{
for (int i = 0; i < 7; i++)
{
domino[i] = Instantiate(dominoPrefab, new Vector3(11, 0, 0), Quaternion.identity) as GameObject;
}
}
}
If you need more details, write a comment. Thanks for help
private List<GameObject> domino = new List<GameObject>(7)
new List<T>(int capacity) constructor doesn't mean the resulting list will have hold capacity objects at beginning. The Count of elements in the list will still be 0.
When you use List class in c#, it will have capacity memory reserved for the list. When you add some elements to the list and its Count reaches capacity(or close to it, I'm not exactly sure), the list will automatically increase capacity by allocating additional memory so that you can add additional element to the list.
Usually when you use new List<T>() the list will have initial capacity of 0 where program doesn't know how many Count the list can possibly have and will dynamically adjust capacity to match your need.
Using new List<T>(int capacity) is like telling the program that the list will have at most capacity number of elements and list should have that capacity ready to avoid overhead of allocating more memory. Of course, when list's Count reach capacity it will increase as well.
To fix your problem, use Add method instead of assigning to array slot.
public class OnlineGame : MonoBehaviour
{
private List<GameObject> domino = new List<GameObject>(7);
public void Start()
{
StartGame();
}
private void StartGame()
{
for (int i = 0; i < 7; i++)
{
// domino[i] = Instantiate(dominoPrefab, new Vector3(11, 0, 0), Quaternion.identity) as GameObject;
domino.Add(Instantiate(dominoPrefab, new Vector3(11, 0, 0), Quaternion.identity) as GameObject);
}
}
}

unity : put clones on array and add back button that destory the last clone till the first

i am very new on unity and im trying to do my first game. i want to put the clone objects on array and then add a back button that destory the last clone that made, ive tried to make array but it let me only clone 1 object and then i cant do anything, thank you for help.
i have this code :
public class TouchSpwan : MonoBehaviour
public GameObject prefab;
public GameObject clone;
public Rigidbody rot;
public Camera mainCamera;
public FixedTouchField touchField;
public float gridsize;
void Update()
{
if (Input.GetMouseButtonDown(0))
{
Vector3 touchPos = mainCamera.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, 9.8f));
touchPos.x = Mathf.Floor(touchPos.x / gridsize);
touchPos.z = Mathf.Floor(touchPos.z / gridsize);
Vector3 result = new Vector3((float)touchPos.x * gridsize,touchPos.y, (float)touchPos.z * gridsize);
if (touchField.Pressed == true)
{
clone = Instantiate(prefab, result, prefab.transform.rotation);
clone.transform.parent = gameObject.transform;
}
}
}
First of all yor GameObject clone, does not have to be a public class member. Instead of
clone = Instantiate(prefab, result, prefab.transform.rotation);
clone.transform.parent = gameObject.transform;
you could just say:
GameObject clone = Instantiate(prefab, result, prefab.transform.rotation);
clone.transform.parent = gameObject.transform;
and get rid of the declaration at the top:
public GameObject clone;
After you check if the left mouse button has been pressed you do an additional check for some touchField.Pressed, not really sure why, but if you remove it you can spawn as many clone objects in a place where your mouse pointer is as you like.
When it comes to storing these object in the array, you can just declare an array of GameObjects and add each clone after instantiating. You do need to have some sort of maxLimit for clones.
declare the arr: private GameObject[] clonesArray;
initialise it: clonesArray = new GameObject[maxNumberOfClones];
Once you've done that you can add each clone to the array after its instantiating:
GameObject clone = Instantiate(prefab, result, prefab.transform.rotation);
clone.transform.parent = gameObject.transform;
clonesArray[currentIndex] = clone;
currentIndex++;
You need to keep track of currentIndex, start with 0 and then increment it after you add a clone to the array. Then to destroy the clone you need to access it from the array and use Destroy(). Do not forget to decrement the index counter.
Destroy(clonesArray[currentIndex]);
currentIndex--;
I hope that helps :D

Unity prefab array instantiate and destroy

I am having a very little problem in my Unity project but can't find a proper help or way to do. I am stuck at point where I have an array of prefab GameObjects and I am trying to instantiate index 1 GameObject and when it destroyed instantiate the next index. Here is how I am doing it. I have two scripts: One to instantiate and other one to destroy it.
Scripts 1:
public class GameObjectsArray : MonoBehaviour {
public static GameObjectsArray Instance { get; set; }
public GameObject[] Objects;
public int i=0;
// Use this for initialization
void Start()
{
InstiatingMethod();
//Instantiate(Objects[i]);
}
public void InstiatingMethod()
{
Instantiate(Objects[i]);
}
}
Scripts 2:
public class CheckDestroy : MonoBehaviour {
//public GameObject[] Objects;
//int i;
// Use this for initialization
void Start () {
Debug.Log("executed");
//Objects = GameObject.FindGameObjectsWithTag("Player");
//OnMouseDown();
//Instantiate(Objects[i], transform.position, transform.rotation);
}
// Update is called once per frame
void Update () {
if (Input.GetMouseButtonDown(0))
{
RaycastHit hit;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit))
{
BoxCollider boxCollider = hit.collider as BoxCollider;
if (boxCollider != null)
{
GameObjectsArray.Instance.i++;
GameObjectsArray.Instance.InstiatingMethod();
Destroy(boxCollider.gameObject);
}
}
}
}
}
So I created a very quick project to make a good response:
Scene Image
In the scene, we will have an empty game object that will contain our script that I called "GameManager", basically this script will do everything, it's more logic to put your logic in one script.
public GameObject[] GameObjects;
private int _targetIndex = -1;
private RaycastHit _hit;
private void Start()
{
InstantiateNextGameObject();
}
public void InstantiateNextGameObject()
{
//if the index is pointing at the last game object in the array, init the index to -1
if (_targetIndex == GameObjects.Length - 1)
_targetIndex = -1;
Instantiate(GameObjects[++_targetIndex]);
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.Mouse0))
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out _hit))
{
BoxCollider boxCollider = _hit.collider as BoxCollider;
if (boxCollider != null)
{
InstantiateNextGameObject();
Destroy(boxCollider.gameObject);
}
}
}
}
we will have an array of gameobjects following with the targetIndex which will start from -1.
The function InstantiateNextGameObject() will simply increment targetIndex and then instantiate a gameobject from the array (++targetIndex the first time will be 0, second time 1 etc). We have to check also if the targetIndex reaches the end of the array, put it back to -1.
Then basically what you did in the update, when you click on a gameobject, instantiate the next one and destroy the current.
At the end, you will get something like that:
https://ayoub-gharbi.org/youba_docs/stackoverflow/stackoverflow01.mp4
Feel free to ask me if you didn't understand anything:)
Happy coding!

Storing variables of GameObjects into Array [Unity]

I have 3 kinds of GameObject, namely blueBook, redBook and greenBook. There are 2 blueBook, 7 redBook and 4 greenBook in the scene.
They are each assigned with the following example script that have 3 properties.
public class blueBook : MonoBehaviour {
public string type = "BlueBook";
public string colour = "Blue";
public float weight;
float s;
void Start () {
float weightValue;
weightValue = Random.value;
weight = Mathf.RoundToInt (700*weightValue+300);
s=weight/1000; //s is scale ratio
transform.localScale += new Vector3(s,s,s);
}
// Update is called once per frame
void Update () {
}
}
In the new class, I want to take all the variables of the GameObject (type, colour, weight) and store them inside an array. How to do this?
After they are all stored inside the array, user will input an weight. Then another class will search through all the info to delete both the array and GameObject(in the scene) with the same amount of weight.
Thank you.
UPDATE
blueBook.cs
public class blueBook: MonoBehaviour {
public string type = "blueBook";
public string colour = "Red";
public float weight;
float s;
void Start () {
float weightValue;
weightValue = Random.value;
weight = Mathf.RoundToInt (500*weightValue+100);
s=weight/1000; //s is scale ratio
transform.localScale += new Vector3(s,s,s);
Debug.Log(weight);
}
// Update is called once per frame
void Update () {
}
}
block.cs
public class block: MonoBehaviour{
public List<GameObject> players;
void Start()
{ Debug.Log(players[1].GetComponent<blueBoook>().weight);
}
// Update is called once per frame
void Update () {
}
The debug.log for block.cs displayed 0 everytime eventho it display otherwise in debug.log of bluebook.cs. It is because it displayed the initial number? I don know wat is wrong
For all blocks in one list you can create a script that has a public list and you drag all your gameobjects into the list in the inspector.
using System.Collections.Generic;
public class ObjectHolder : MonoBehaviour
{
public List<GameObject> theBooks;
// You can remove by weight e.g. like this
public void DeleteByWeight(float inputWeight)
{
for(int i = theBooks.Count - 1; i >= 0; i--)
{
if(theBooks[i].GetComponent<Book>().weight == inputWeight)
Destroy(theBooks[i]);
theBooks.RemoveAt(i);
}
}
}
}
The script on the blocks needs to be renamed to the same name for all (Book in my example). From your code that is no problem since they only differ in the value of the members.

Accessing a variable in another scene

Is it possible to change a variable in another scene in unity. I have a script right now that has the user pick 5 heroes and those 5 heroes get saved to a array, but in order for the game to run how i want it, that array will be in another scene and I'm not sure how to go about saving the five heroes data to an array in another scene. I can do it all in one scene but 2 scenes would be more efficient. Here's my code:
using UnityEngine;
using System.Collections;
public class HeroChooser : MonoBehaviour {
public static GameObject Archer;
GameObject Berserker;
GameObject Rouge;
GameObject Warrior;
GameObject Mage;
GameObject MainCamera;
public int counter = 0;
public bool archerOn = false;
public bool berserkerOn = false;
public bool rougeOn = false;
public bool mageOn = false;
public bool warriorOn = false;
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
void OnGUI(){
if(archerOn == false){
if (GUI.Button (new Rect(50,0,50,50), "Archer")){
Archer = GameObject.Find("Archer");
MainCamera = GameObject.Find("Main Camera");
HeroArraySaver heroArraySaver = MainCamera.GetComponent<HeroArraySaver>();
heroArraySaver.array[counter] = Archer;
archerOn = true;
counter++;
}
}
Its saying that: Static member HeroArraySaver.array cannot be accessed with an instance reference, qualify it with a type name instead im not sure how to go about fixing it.
A simple way would be to create an empty GameObject and attach a script/MonoBehaviour to that which holds your data. To make it persist you would have to call DontDestroyOnLoad() on that GameObject. This will ensure your GameObject will hang around when moving to a different scene.
So something like:
GameObject myPersistentDataObject = new GameObject("myPersistentDataObject");
MyDataClass data_class = myPersistentDataObject.AddComponent<MyDataClass>();
//set your data to whatever you need to maintain
And in your Awake of your MyDataClass you'd do something like
void Awake()
{
DontDestroyOnLoad(transform.gameObject);
}
Then in your other scene you can simply find your GameObject again and retrieve its data from the attached component.
Assuming you have integer IDs for the heroes, simply store them in a static variable:
public class GlobalData {
public static int[] heroIds;
}
Static variables can be accessed from any scene and will persist as long as your game runs. The same technique works for strings or enums.

Resources