Mam spory problem ze skryptem, może moglibyście by mi pomóc.
Mam skrypt SelectionManager.cs (zamieszczony na samym dole posta). Konkretnie problemem jest metoda DeselectAll(). Nie dostaję żadnych błędów, ale w Playmode widzę, że mimo odznaczenia jednostek do nowego punktu docelowego przemieszczają się wszystkie, nawet jeśli po odznaczeniu zaznaczę zupełnie inne. Tak jakby wszystkie jednostki, które zostały dodane do listy selectedObjects, nie mogły zostać stamtąd wyrzucone. Czyli linijka selectedObjects.Clear() z jakiegoś powodu jest całkowicie ignorowana. Ok, myślę sobie. W takim razie usunąłem tę linijkę i wstawiłem do pętli foreach selectedObjects.remove (obj). W playmode za każdym razem dostaję error (z pauzą, bo tak sobie ustawiłem żeby wiedzieć, gdzie dokładnie problem występuje), który brzmi:
InvalidOperationException: Collection was modified; enumeration operation may not execute. System.Collections.Generic.List` 1+Enumerator[UnityEngine.GameObject].VerifyState() (at /…/List.cs:778)
Zacząłem pytać na forum.unity.com i tam mi dwie osoby wyjaśniły, że nie mogę w ten sposób modyfikować tej listy, a jeśli chcę ją “wyczyścić”, powinienem zamiast selectedObjects.Clear() użyć selectedObjects = new List(). Jeszcze nie dostałem odpowiedzi, ale to też nie działa. Spróbowałem “na chama” wstawić obie linijki, najpierw z new, pod spodem z Clear. Co się okazało? (dla uproszczenia zawężam problem do dwóch kostek)
- Jeśli zaznaczę A i/lub B, odznaczę i kliknę by wyznaczyć cel - nic się nie dzieje, A i B zostaje na miejscu - DOBRZE
- Jeśli zaznaczę kostkę A, odznaczę ją, zaznaczę B i wyznaczę cel - tylko B się tam przesuwa - DOBRZE.
- Jeśli zaznaczę kostkę A, wyznaczę jej cel, odznaczę, zaznaczę B i wyznaczę nowy cel - oba się przesuwają do drugiego celu - BARDZO ŹLE.
Coś mi umknęło? Jak to ominąć?
Obiecany kod:
[CODE]using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SelectionManager : MonoBehaviour {
public Transform target;
[HideInInspector]
private Vector3 startSquare;
[HideInInspector]
private Vector3 endSquare;
[SerializeField]
public LayerMask selectablesLayer;
[HideInInspector]
public List<GameObject> selectedObjects;
[HideInInspector]
public List<GameObject> selectableObjects;
private float widthSquare;
private float heightSquare;
void Awake (){
selectedObjects = new List<GameObject> ();
selectableObjects = new List<GameObject> ();
}
void Start () {
}
void Update () {
if (Input.GetMouseButtonDown (0)) {
startSquare = Camera.main.ScreenToViewportPoint (Input.mousePosition);
}
if (Input.GetMouseButtonUp (0)) {
endSquare = Camera.main.ScreenToViewportPoint (Input.mousePosition);
if (endSquare != startSquare) {
SelectInArea ();
}
else {
SelectMe ();
}
}
if (Input.GetMouseButtonUp (1)) {
DeselectAll ();
}
}
void SelectMe (){
RaycastHit hit;
if (Physics.Raycast (Camera.main.ScreenPointToRay (Input.mousePosition), out hit, selectablesLayer)) {
if (hit.collider.gameObject.layer == 8) {
if (!(Input.GetKey (KeyCode.LeftShift))) {
DeselectAll ();
}
SelectionComponent clickOnScript = hit.collider.GetComponent<SelectionComponent> ();
if (clickOnScript.currentlySelected == false) {
selectedObjects.Add (hit.collider.gameObject);
clickOnScript.currentlySelected = true;
clickOnScript.SelectMe ();
}
}
else {
MoveToTarget ();
}
}
}
void SelectInArea(){
widthSquare = endSquare.x - startSquare.x;
heightSquare = endSquare.y - startSquare.y;
if (!(Input.GetKey (KeyCode.LeftShift)) && Mathf.Abs (widthSquare) > 0.02 && Mathf.Abs (heightSquare) > 0.02) {
DeselectAll ();
}
Rect selectSquare = new Rect (startSquare.x, startSquare.y, widthSquare, heightSquare);
foreach (GameObject selectObject in selectableObjects) {
if (selectSquare.Contains (Camera.main.WorldToViewportPoint (selectObject.transform.position), true)) {
selectedObjects.Add (selectObject);
selectObject.GetComponent<SelectionComponent> ().currentlySelected = true;
selectObject.GetComponent<SelectionComponent> ().SelectMe ();
}
}
}
void DeselectAll() {
foreach (GameObject obj in selectedObjects) {
obj.GetComponent<SelectionComponent> ().currentlySelected = false;
obj.GetComponent<SelectionComponent> ().SelectMe();
}
selectedObjects.Clear ();
}
void MoveToTarget (){
RaycastHit targetPoint;
Vector3 targetPosition;
if (Physics.Raycast (Camera.main.ScreenPointToRay (Input.mousePosition), out targetPoint, Mathf.Infinity)) {
targetPosition.x = targetPoint.point.x;
targetPosition.y = -10f;
targetPosition.z = targetPoint.point.z;
foreach (GameObject obj in selectedObjects) {
Instantiate (target);
target.transform.position = Vector3.MoveTowards(transform.position, targetPosition, Mathf.Infinity);
AIPath aiPathScript = obj.GetComponent<AIPath> ();
aiPathScript.target = target;
DynamicGridObstacleAssistant unitObstacleScript = obj.GetComponent<DynamicGridObstacleAssistant> ();
unitObstacleScript.isMoving = true;
}
}
}
}
[/CODE]
Od razu mówię, że mam świadomość faktu, że po wyznaczeniu nowego celu stary obiekt “pozostaje” - jak się uporam z przedstawionym problemem, zamierzam tę funkcję dodać.