I don't know how to check for position of gameObjects in Unity - c#

What I'm building is a Tetris word game where you have a goal word that you need to spell but you also get points for spelling other word with those letters. (for example the goal word is seaweed and you get points for spelling words like sea and weed and you win once you actually spell seaweed). Anyway, what I have right now is the starting of a tetris game without the part where it clears a line (because I want a word to clear, not a line). Each letter is its own gameObject and I don't know how to check for words. I get how to add to the score and everything, but it's the checking for words that has me stuck. This is what the game looks like sort of:
Screenshot of my screen in Unity
I was thinking maybe I could check if the gameObject for each letter is in a specific sequence (positioned next to other letters that would spell out a word) but I don't know how to do that or if it would even work. If anyone could help me figure this one out I would really appreciate it. Here's the code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TetrisBlock : MonoBehaviour
{
private float previousTime;
public float fallTime = 0.8f;
public static int height = 280;
public static int width = 160;
private static Transform[,] grid = new Transform[width, height];
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.LeftArrow))
{
transform.position += new Vector3(-20, 0, 0);
if (!ValidMove())
{
transform.position += new Vector3(20, 0, 0);
}
}
else if (Input.GetKeyDown(KeyCode.RightArrow))
{
transform.position += new Vector3(20, 0, 0);
if (!ValidMove())
{
transform.position += new Vector3(-20, 0, 0);
}
}
if(Time.time - previousTime > (Input.GetKey(KeyCode.DownArrow) ? fallTime / 10 : fallTime))
{
transform.position += new Vector3(0, -20, 0);
if (!ValidMove())
{
transform.position -= new Vector3(0, -20, 0);
AddToGrid();
this.enabled = false;
FindObjectOfType<SpawnerTetromino>().NewTetromino();
}
previousTime = Time.time;
}
void AddToGrid()
{
int roundedX = Mathf.RoundToInt(transform.position.x);
int roundedY = Mathf.RoundToInt(transform.position.y);
grid[roundedX, roundedY] = transform;
}
bool ValidMove()
{
{
int roundedX = Mathf.RoundToInt(transform.position.x);
int roundedY = Mathf.RoundToInt(transform.position.y);
if (roundedX < 0 || roundedX >= width || roundedY < 0 || roundedY >= height)
{
return false;
}
if (grid[roundedX, roundedY] != null)
return false;
}
return true;
}
}
}

yes, you can definitely do this.
First, you need to track the GameObject or Transform of the letters, for example: Transform[] letters
Then, you need to set their position, for example:
int width = 100;
for(int i = 0; i < letters.Length; i++)
{
letters[i].position = new Vector3(i*width, 0):
}
Another thing that you can do is, have a prefab of each letter perhaps, even have an array of 26 where 0=A and 25=Z then instantiate the letters based on the word and place them in order.
A final idea, is to Instantiate your letters into an object with a Horizontal Layout Group.

Related

Set behaviours(script a) controlled by booleans(script b) based on Time.time (flocking system)

I am working on a flocking system in Unity and am new to c#. I am working with 2 scripts - 1 that manages the overall flock (FlockTest) and the other that manages particle behaviour (FlockParticleBehaviour). I have followed a tutorial which has public boolean values that control seeking behaviour in FlockParticleBehaviour through FlockTest. In play mode, I can toggle these booleans to change the goal seeking behaviour. However, I want to automate this toggling based on time (To add it to an AR session). I have added an if statement to void Update() in the FlockTest and when I hit play, the seekGoal and obedient boolean boxes switch on and off but nothing happens to the particles. I have tried using an invoke method which didn't work(no errors but boxes dont switch on and off) and thought about trying a coRoutine but I am not sure this will work since I don't want to stop and start my script. I am at a loss as to how to get the particles obeying the boolean in update. Am I meant to be referencing in my particle behaviour script's flock function? Very new so would love some help if anyone knows a better way forward!
FlockTest script (contains if statement)
using System.Collections.Generic;
using UnityEngine;
public class FlockTest : MonoBehaviour
{
public GameObject[] particles;
public GameObject particlePrefab;
public int particleCount = 10;
public Vector3 range = new Vector3(5,5,5);
public Vector3 innerLimit = new Vector3(1,1,1);
public bool seekGoal = true;
public bool obedient = true;
public bool willful = false;
[Range(0, 200)]
public int neighbourDistance =50;
[Range(0,2)]
public float maxForce = 0.5f;
[Range(0,5)]
public float maxvelocity = 2.0f;
// Start is called before the first frame update
void Start()
{
int time = (int)Time.time;
particles = new GameObject[particleCount];
for(int i = 0; i < particleCount; i++)
{
Vector3 particlePos = new Vector3(Random.Range(-range.x, range.x), Random.Range(-range.y, range.y), Random.Range(-range.z, range.z));
particles[i] = Instantiate(particlePrefab, this.transform.position + particlePos, Quaternion.identity) as GameObject;
particles[i].GetComponent<FlockParticleBehaviour>().manager = this.gameObject;
}
}
void Update()
// the toggles in the inspector are changing but nothing is happening with the particles.
{
int time = (int)Time.time;
if(time == 3f) {
seekGoal = false;
obedient = false;
willful = true;
}
if(time == 6f)
{
seekGoal = true;
obedient = true;
willful = false;
}
}
}
FlockParticleBehaviour script
using System.Collections.Generic;
using UnityEngine;
public class FlockParticleBehaviour : MonoBehaviour
{
public GameObject manager;
public Vector3 location = Vector3.zero;
public Vector3 velocity;
Vector3 goalPos = Vector3.zero;
Vector3 currentForce; //this is a current force position. pushes particle around by adding all the other forces
// Start is called before the first frame update
void Start()
{
velocity = new Vector3(Random.Range(0.01f, 0.1f), Random.Range(0.01f, 0.1f), Random.Range(0.01f, 0.1f));
location = new Vector3(this.gameObject.transform.position.x, this.gameObject.transform.position.y, this.gameObject.transform.position.z);
}
Vector3 seek(Vector3 target)
{
return(target - location);
}
void applyForce(Vector3 f)
{
Vector3 force = new Vector3(f.x, f.y, f.z);
if(force.magnitude > manager.GetComponent<FlockTest>().maxForce)
{
force = force.normalized;
force *= manager.GetComponent<FlockTest>().maxForce;
}
this.GetComponent<Rigidbody>().AddForce(force);
if(this.GetComponent<Rigidbody>().velocity.magnitude > manager.GetComponent<FlockTest>().maxvelocity)
{
this.GetComponent<Rigidbody>().velocity = this.GetComponent<Rigidbody>().velocity.normalized;
this.GetComponent<Rigidbody>().velocity *= manager.GetComponent<FlockTest>().maxvelocity;
}
Debug.DrawRay(this.transform.position, force, Color.white);
}
Vector3 align()
{
float neighbourdist = manager.GetComponent<FlockTest>().neighbourDistance;
Vector3 sum = Vector3.zero;
int count = 0;
foreach (GameObject other in manager.GetComponent<FlockTest>().particles)
{
if(other == this.gameObject) continue;
float d = Vector3.Distance(location, other.GetComponent<FlockParticleBehaviour>().location);
if (d < neighbourdist) {
sum += other.GetComponent<FlockParticleBehaviour>().velocity;
count++;
}
}
if (count >0)
{
sum /= count;
Vector3 steer = sum - velocity;
return steer;
}
return Vector3.zero;
}
Vector3 cohesion()
{
float neighbourdist = manager.GetComponent<FlockTest>().neighbourDistance;
Vector3 sum = Vector3.zero;
int count = 0;
foreach (GameObject other in manager.GetComponent<FlockTest>().particles)
{
if(other == this.gameObject) continue;
float d = Vector3.Distance(location, other.GetComponent<FlockParticleBehaviour>().location);
if(d < neighbourdist)
{
sum += other.GetComponent<FlockParticleBehaviour>().location;
count++;
}
}
if (count > 0)
{
sum /= count;
return seek(sum);
}
return Vector3.zero;
}
void flock()
{
location = this.transform.position;
velocity = this.GetComponent<Rigidbody>().velocity;
if(manager.GetComponent<FlockTest>().obedient && Random.Range(0,50) <=1)
{
Vector3 ali = align();
Vector3 coh = cohesion();
Vector3 gl;
if(manager.GetComponent<FlockTest>().seekGoal)
{
gl = seek(goalPos);
currentForce = gl + ali +coh;
}
else
currentForce = ali + coh;
currentForce = currentForce.normalized;
}
if(manager.GetComponent<FlockTest>().willful && Random.Range(0,50)<=1)
{
if(Random.Range(0,50)<1) //change direction
currentForce = new Vector3(Random.Range(0.01f, 0.1f), Random.Range(0.01f, 0.1f),Random.Range(0.01f, 0.1f));
}
applyForce(currentForce);
}
// Update is called once per frame
void Update()
{
flock();
goalPos = manager.transform.position;
}
}
Several points:
it is much easier and cleaner to set your flock manager directly as FlockTest, not GameObject to avoid GetComponent calls.
I cannot understand what you want to achieve by calling (int)Time.time and comparing it later with 3 and 6. Time.time returns the number of seconds that passed from the start of the application. So your code in Update method of FlockTest script will not have any chance to be called after the seventh second of your game passed. So obedient will always be true and willful will always be false after the seventh second.
Your Random.Range(0, 50) <= 1 is quite a low chance. It will return an int value from 0 to 49, so it is only a 2% chance that your changes in FlockTest will apply to FlockParticleBehaviour instance. Is it what you wanted to get? You can try to remove this random from the if statement to make this chance 100% and check if this is an issue.
Right now it seems like the chance of changing something is too low to see it in several seconds of the game. As I've said above, after the seventh second your bool values will never change.

How to get Panel to show when character lands on a specific position on game board after dice roll in Unity

I have a 3D board game in Unity. I would like to move my character without having to press a key, but most importantly I would like to show a dynamic panel in canvas for whatever square the character lands on. So far I have the dice rolling and the character moving (after pressing a key) the correct amount of squares, but I am unable to figure out how to activate the panel based on the square color. Any help would be appreciated.
Here is my CharacterScript:
public class CharacterScript : MonoBehaviour
{
public Path currentPath;
public int squarePosition;
public int squares;
bool isMoving;
public GameObject PinkSquarePanel = GameObject.FindGameObjectWithTag("PinkSquare");
public GameObject CyanSquarePanel = GameObject.FindGameObjectWithTag("CyanSquare");
public GameObject WhiteSquarePanel = GameObject.FindGameObjectWithTag("WhiteSquare");
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.Escape) && !isMoving)
{
squares = DiceNumberTextScript.diceNumber;
}
StartCoroutine(Move());
if (squares == 0)
{
ActivateSquarePanel();
}
}
IEnumerator Move()
{
if (isMoving)
{
yield break;
}
isMoving = true;
while (squares > 0)
{
Vector3 nextPosition = currentPath.squareList[squarePosition + 1].position;
while (MoveToNextSquare(nextPosition))
{
yield return null;
}
yield return new WaitForSeconds(0.1f);
squares--;
squarePosition++;
}
isMoving = false;
}
bool MoveToNextSquare(Vector3 goal)
{
return goal != (transform.position = Vector3.MoveTowards(transform.position, goal, 4f * Time.deltaTime));
}
void ActivateSquarePanel()
{
if (squarePosition.Equals(PinkSquarePanel))
{
PinkSquarePanel.GetComponent<CanvasGroup>().alpha = 1;
}
else if (squarePosition.Equals(CyanSquarePanel))
{
CyanSquarePanel.GetComponent<CanvasGroup>().alpha = 1;
}
else if (squarePosition.Equals(WhiteSquarePanel))
{
WhiteSquarePanel.GetComponent<CanvasGroup>().alpha = 1;
}
}
}
And here is my PathScript:
public class Path : MonoBehaviour
{
Transform[] squareObjects;
public List<Transform> squareList = new List<Transform>();
GameObject[] PinkSquares = GameObject.FindGameObjectsWithTag("PinkSquare");
PinkSquare[] pinkList = new PinkSquare[1];
GameObject[] CyanSquares = GameObject.FindGameObjectsWithTag("CyanSquare");
CyanSquare[] cyanList = new CyanSquare[1];
GameObject[] WhiteSquares = GameObject.FindGameObjectsWithTag("WhiteSquare");
WhiteSquare[] whiteList = new WhiteSquare[1];
private void OnDrawGizmos()
{
Gizmos.color = Color.black;
FillSquares();
for (int i = 0; i < squareList.Count; i++)
{
Vector3 currentPosition = squareList[i].position;
if (i > 0)
{
Vector3 previousPosition = squareList[i - 1].position;
Gizmos.DrawLine(previousPosition, currentPosition);
if(currentPosition.Equals(PinkSquares))
{
pinkList[i] = new PinkSquare();
}
else if (currentPosition.Equals(CyanSquares))
{
cyanList[i] = new CyanSquare();
}
else if (currentPosition.Equals(WhiteSquares))
{
whiteList[i] = new WhiteSquare();
}
}
}
}
void FillSquares()
{
squareList.Clear();
squareObjects = GetComponentsInChildren<Transform>();
foreach (Transform square in squareObjects)
{
if (square != this.transform)
{
squareList.Add(square);
}
}
}
}
I believe your issue is in your comparisons, you are trying to use an Equals to compare your currentPosition which is a Vector3 type to a GameObject[] which is an array of gameObjects. As I mentioned in my comments, this comparison will always fail as an array of gameObjects can not be equal to a vector.
Instead of using these lines, try this line:
if(squareList[i].gameObject.tag.CompareTag("PinkSquare")
The full snippet of if else would look like
if(squareList[i].gameObject.tag.CompareTag("PinkSquare")
{
pinkList[i] = new PinkSquare();
}
else if(squareList[i].gameObject.tag.CompareTag("CyanSquare")
{
cyanList[i] = new CyanSquare();
}
else if(squareList[i].gameObject.tag.CompareTag("WhiteSquare")
{
whiteList[i] = new WhiteSquare();
}
Your CharacterScript is going to need to get the gameObject or Transform from the Path script as it is only keeping track of indexes. Your issue in this script is you are comparing an integer to a GameObject which would never be true. I would also recommend not using OnDrawGizmos() as it is an editor only script and should only be used to render editor debugging tools. The only reference to a Gizmo I see in the function is Gizmos.color = Color.black; which does nothing as you are not rendering a gizmo anywhere. I would move this code to a different function and call it from your CharacterScript. Have the return type be GameObject or Transform of the square you are on, so the CharacterSCript can check which color it lands on. Using an Integer nor Vector3 to compare to a GameObject[] will never work.
I am not sure if there are issues elsewhere in the code, but as this comparison would always fail, none of these statements would get broken into. What this means is your panels would never have the chance to get their alpha set nor get created.

Unity - Move (Shuffle) multiple GameObject from their current position to a new destination position

I am trying to do a shuffle functionality so I can change the order of multiple GameObjects.
I have multiple "letters" (GameObjects) in a circle and when I click "Shuffle" I just need to reorder their position but showing the letter moving to it's new position.
I tried: Vector3.Lerp(startPosition, targetPosition, Time.time/2)
or
Vector3.MoveTowards(startPosition, targetPosition, Time.time/2)
but it doesn't move correctly.
I manage to do the movement using this:
Vector3 tempPosition = object1.transform.position;
object1.transform.position = object2.transform.position;
object2.transform.position = tempPosition;
but I can only move 2 GameObjects without showing any movement.
Get a list of all letters, use a coroutine to move the letters, and at the beginning of the coroutine, figure out where each letter is moving from and moving to, then use lerp in each frame to set the position:
public float letterMoveTime = 1f; // duration of shuffle movement (in seconds)
List<GameObject> letters;
IEnumerator shuffleCoroutine;
void Awake()
{
letters = new List<GameOobject>();
letters.AddRange(GameObject.FindGameObjectsWithTag("Letter"));
shuffleCoroutine = null;
}
public void StartShuffle() // call this on button click
{
if (shuffleCoroutine != null) return;
shuffleCoroutine = DoShuffle();
StartCoroutine(shuffleCoroutine);
}
IEnumerator DoShuffle()
{
List<Vector3> startPos = new List<Vector3>();
List<Vector3> endPos = new List<Vector3>();
foreach (GameObject letter in letters)
{
startPos.Add(letter.transform.position);
endPos.Add(letter.transform.position);
}
// shuffle endPos
for (int i = 0 ; i < endPos.Count ; i++) {
Vector3 temp = endPos[i];
int swapIndex = Random.Range(i, endPos.Count);
endPos[i] = endPos[swapIndex];
endPos[swapIndex] = temp;
}
float elapsedTime = 0f;
while (elapsedTime < letterMoveTime)
{
// wait for next frame
yield return null;
// move each letter
elapsedTime = Mathf.Min(letterMoveTime, elapsedTime+Time.deltaTime);
float t = elapsedTime / letterMoveTime;
for (int i = 0 ; i < startPos.Count ; i++) {
letter[i].transform.position = Vector3.lerp(startPos[i],endPos[i],t);
}
}
// allow shuffling to occur again
shuffleCoroutine = null;
}

bool stays false even when called to change

UPDATE
this is not the main problem I had. check out this question
I've made a little multiplayer game in unity, and I want to know, when the opponent player is dead (bool oppdead).
If I run my code, and the opponent player dies, I do get the Log "opp player is dead", but my onGUI isnt beeing executed. Have I done something wrong? all my other bool's work perfectly..
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using GooglePlayGames.BasicApi.Multiplayer;
using UnityEngine.UI;
using System;
public class BirdMovementMP : MonoBehaviour, MPLobbyListener
{
public GameObject opponentPrefab;
public Rigidbody2D oppPlane;
private bool _multiplayerReady;
private string _myParticipantId;
public bool oppdead = false;
public float flapSpeed = 100f;
public float forwardSpeed = 1f;
bool didFlap = false;
Animator animator;
public bool dead = false;
float deathCooldown;
public Rigidbody2D plane;
public Button buttonImage;
public GUISkin replay;
public GUISkin home;
void SetupMultiplayerGame()
{
MultiplayerController.Instance.lobbyListener = this;
// 1
_myParticipantId = MultiplayerController.Instance.GetMyParticipantId();
// 2
List<Participant> allPlayers = MultiplayerController.Instance.GetAllPlayers();
_opponentScripts = new Dictionary<string, OpponentPlane>(allPlayers.Count - 1);
for (int i = 0; i < allPlayers.Count; i++)
{
string nextParticipantId = allPlayers[i].ParticipantId;
Debug.Log("Setting up car for " + nextParticipantId);
// 3
if (nextParticipantId == _myParticipantId)
{
// 4
//rigidbody2D.GetComponent<BirdMovementMP>().SetCarChoice(i + 1, true);
// myCar.transform.position = carStartPoint;
}
else
{
// 5
/* GameObject OpponentPlane = (Instantiate(opponentPrefab, carStartPoint, Quaternion.identity) as GameObject);
OpponentPlane opponentScript = OpponentPlane.GetComponent<OpponentPlane>();
opponentScript.SetCarNumber(i + 1);
// 6
_opponentScripts[nextParticipantId] = opponentScript;*/
}
}
// 7
_multiplayerReady = true;
}
public void UpdatePlane(string senderId, float x, float y, float z, bool death)
{
MultiplayerController.Instance.GetMyParticipantId();
// OpponentPlane opponent = _opponentScripts[senderId];
if (death)
{
Debug.Log("opp Player is dead");
oppdead = true;
}
if (opponentPrefab != null)
{
opponentPrefab.transform.position = new Vector3(x, y, 0);
opponentPrefab.transform.rotation = Quaternion.Euler(0, 0, z);
Debug.Log("setting opp pos new");
}
if (opponentPrefab == null)
{
// Debug.Log("oppo is gleich null");
opponentPrefab = GameObject.Find("Opponent");
opponentPrefab.transform.position = new Vector3(x, y, 0);
opponentPrefab.transform.rotation = Quaternion.Euler(0, 0, z);
}
}
void doMultiplayerUpdate()
{
MultiplayerController.Instance.SendMyUpdate(plane.transform.position.x,
plane.transform.position.y,
plane.transform.rotation.eulerAngles.z,
dead);
// Debug.Log("Im at position:" + plane.transform.position.x + "x" + plane.transform.position.x + "y");
}
// Use this for initialization
void Start()
{
if(opponentPrefab == null)
{
opponentPrefab = GameObject.Find("Opponent");
}
animator = transform.GetComponentInChildren<Animator>();
Time.timeScale = 0;
if (animator == null)
{
Debug.LogError("Didn't find animator!");
}
}
void OnGUI()
{
if (oppdead)
{
GUI.skin.label.fontSize = Screen.height / 20;
GUI.Label(new Rect(Screen.height / 2, Screen.height / 2, Screen.height / 2, Screen.height / 2), " other is deadddd ");
}
if (dead)
{
//Menu Button
GUI.skin = null;
GUI.skin = home;
if (GUI.Button(new Rect(10, Screen.height / 2, Screen.height / 4, Screen.height / 4), ""))
{
Application.LoadLevel("home");
}
}
}
// Do Graphic & Input updates here
void Update()
{
doMultiplayerUpdate();
if (dead)
{
deathCooldown -= Time.deltaTime;
}
else
{
if (Input.GetKeyDown(KeyCode.Space) || Input.GetMouseButtonDown(0))
{
didFlap = true;
}
}
}
// Do physics engine updates here
void FixedUpdate()
{
if (dead)
return;
plane.AddForce(Vector2.right * forwardSpeed);
if (didFlap)
{
plane.AddForce(Vector2.up * flapSpeed);
animator.SetTrigger("DoFlap");
didFlap = false;
}
if (plane.velocity.y > 0)
{
transform.rotation = Quaternion.Euler(0, 0, 0);
}
else
{
float angle = Mathf.Lerp(0, -90, (-plane.velocity.y / 3f));
transform.rotation = Quaternion.Euler(0, 0, angle);
}
}
void OnCollisionEnter2D(Collision2D collision)
{
animator.SetTrigger("Death");
dead = true;
deathCooldown = 0.5f;
}
}
(One) problem you face is simple, DO NOT set a default value for "Inspector variables" (ie, when you have "public"). Explanation.
IF (see below) you need an Inspector variable, you simply cannot do this:
public int example = 3;
you must do this
public int example;
Further to your specific case Emanuel. You need to try two things. (A) there is absolutely no reason to have an Inspector variable here. Please change to this:
public bool oppdead = false;
change to
[System.NonSerialized] public bool oppdead = false;
It's one of those odd things in Unity. In practice there is very little reason to use "Inspector" variables other than in test projects. So when you need an ordinary, everyday public variable in c#, you have to declare it as
[System.NonSerialized] public
rather than just
public
So, you see this everywhere in Unity source files. So in the first case "A" try that.
In the second case and conversely.
One simple possibility is it is highly likely some other process is changing the variable, since, you have marked it public. You must change it to a local variable.
private bool oppdead
Try this and see what happens.
Note that if you are an experienced programmer new to Unity, Unity can be incredibly confusing since, classes mean less than nothing in Unity; you may have a component that "changes oppdead" BUT who knows how many game objects and which ones have that component attached and running; anything could be changing the value. For this reason go with private.
The next issue is, as you say, it's impossible to debug multiplayer games properly as it's hard to access development messages. You must fix this problem, and I will show you how.
click GameObject, UI, Canvas, select "scale with screen size"
on that click GameObject, UI, Text. position it towards the top left. tip, be sure to choose these two items like this:
be sure to name the Text item "devText"
Use code roughly like this to display ongoing development messages:
public void devMessage(string m)
{
Text t = GameObject.Find("devText").GetComponent<Text>();
t.text = t.text +"\n" +m
}
(you can call it from any object.)
at the point where you "Debug.Log("opp Player is dead");", use the devMessage system instead.
Let us know what happens, particularly when you change to "private".
I remind you that if you are actually using it as a public variable and changing it from elsewhere, you have completely wasted everyone's time here as you don't show the code that is doing that :)
A reminder that you must not make it an "Inspector" variable, for any reason.
Pls let us know the latest!

C# -> Index was out of range error | Using Lists

I am using XNA to make a tank game. I've implemented a feature to shoot bullets using a list. After shooting, I want to test if the bullet has gotten close to the boundaries of the screen. If so, remove that particular bullet from the list.
The error appears only when I have more than one bullet on the screen at any given time. Here is the code:
Tank Class:
List<Bullet> bulletList = new List<Bullet>();
bool spacebarPrepared = true; //for shooting every 0.5 seconds
short count = 0;
//Shoot
if (Keyboard.GetState().IsKeyDown(Keys.Space) && spacebarPrepared == true)
{
bulletList.Add(new Bullet(sprBullet, position, turretDirection, turretAngle));
spacebarPrepared = false;
}
if (spacebarPrepared == false)
{
spacebarCount += (float)gameTime.ElapsedGameTime.TotalSeconds;
if (spacebarCount > 0.5)
{
spacebarPrepared = true;
spacebarCount = 0;
}
}
//Update bullets
foreach (Bullet bullet in bulletList)
{
bullet.Update(bounds);
}
count = (short)bulletList.Count;
//Remove unwanted bullets
for (short i = 0; i < count; i++)
{
if (bulletList[i].Alive == false)
{
bulletList.Remove(bulletList[i]);
}
}
Bullet Class:
class Bullet
{
Texture2D spr;
Vector2 origin, pos, dir, turretLength;
float rotation, scale, turretLeng;
short speed;
bool alive = true;
public Bullet(Texture2D sprite, Vector2 position, Vector2 direction, float angle)
{
spr = sprite;
scale = 0.15f;
turretLeng = (110 + spr.Width) * scale;
speed = 5;
rotation = angle;
pos = position;
dir = direction;
origin = new Vector2(spr.Width / 2, spr.Height / 2);
FindTurretLength();
}
public void Draw(SpriteBatch spriteBatch)
{
Matrix bulletTranslation = Matrix.CreateRotationZ(rotation) * Matrix.CreateTranslation(pos.X + turretLength.X, pos.Y + turretLength.Y, 0);
spriteBatch.Begin(SpriteSortMode.BackToFront, null, null, null, null, null, bulletTranslation);
spriteBatch.Draw(spr, Vector2.Zero, null, Color.White, 0, origin, 0.15f, SpriteEffects.None, 1f);
spriteBatch.End();
}
public void Update(Vector2 boundary)
{
pos += dir * speed;
if (pos.X < 50 || pos.X > boundary.X - 50 || pos.Y < 50 || pos.Y > boundary.Y - 50)
{
alive = false;
}
}
public void FindTurretLength()
{
turretLength = new Vector2(turretLeng * dir.X, turretLeng * dir.Y);
}
public Vector2 Pos
{
get
{
return pos;
}
set
{
pos = value;
}
}
public bool Alive
{
get
{
return alive;
}
set
{
alive = value;
}
}
}
What I noticed myself when debugging was that my own 'count' variable = 2, yet the bulletList.Count = 1. Could that be the problem? How is that occurring?
Any help is greatly appreciated.
The problem is in your for loop that removes bullets.
Lets say you have a list of 10 bullets (indexes 0 - 9) when you start the loop. The 1st bullet (at index 0) gets removed....now your list has 9 bullets (indexes 0 - 8), but the count variable has not been updated so your for loop still thinks it has 10.
When you reach the point where "i" is greater than the ACTUAL amount of bullets alive, you'll get the "Index was out of range." error.
There are multiple ways you can fix this error.
I would go for:
bulletList.RemoveAll(x => !x.Alive);

Categories

Resources