Continuing with my game here (a game where the player is a ship and you shoot down meteors). I am currently working on the ship shooting bullets and I am trying to remove the meteors as they get hit by the bullets. So here's what I've done. I am almost there, but I have found an error in my coding. In my game, there spawns meteors outside the right side of the map and they travel to the left and the point is to shoot them down, this is working if you shoot the meteors in right order but not otherwise. Let me explain it with a picture.
If I were to shoot down the second meteor, the meteor marked with number 1 would get destroyed
first
//spawns the enemies.
public void LoadEnemies()
{
int randY = random.Next(100, 500);
if (spawn > 1)
{
spawn = 0;
if (enemies.Count() < 4)
enemies.Add(new Enemies(Content.Load<Texture2D>("meteor"), new Vector2(1110, randY)));
}
//Here's where the error lies because of the bulletcolliding (I think)
for (int i = 0; i < enemies.Count; i++)
{
if (!enemies[i].isVisible || bulletColliding)
{
bulletColliding = false;
enemies.RemoveAt(i);
i--;
}
}
}
Collision method.
public void bulletCollision(GameTime gameTime)
{
foreach (var x in bullets)
{
foreach (var y in enemies)
{
enemy_rect = new Rectangle((int)y.position.X, (int)y.position.Y, 10, 10);
bullet_rect = new Rectangle((int)x.position.X, (int)x.position.Y, 10, 10);
if (bullet_rect.Intersects(enemy_rect))
{
bulletColliding = true;
}
}
}
}
Basically, I am clueless on how to remove the specific meteor that gets hit, and I need your help. I Appreaciate all help I get.
DonĀ“t use only bool bulletColliding. Use also integer which shows the number of enemy which should be destroyed and in each enemy class keep information of his number.
The second option can be keeping a bool ifDestroy in enemy class which will be false initially, and change it to true if the enemy should be destroyed. You can check it in loadEnemies().
Basically this is a further explanation of Sabrina Le's answer.
You want the enemy objects themselves to have a bool called bulletColliding and in your collision code it would look more like...
public void bulletCollision(GameTime gameTime)
{
foreach (var x in bullets)
{
foreach (var y in enemies)
{
enemy_rect = new Rectangle((int)y.position.X, (int)y.position.Y, 10, 10);
bullet_rect = new Rectangle((int)x.position.X, (int)x.position.Y, 10, 10);
if (bullet_rect.Intersects(enemy_rect))
{
y.bulletColliding = true;
}
}
}
}
and then your enemy loading code would look more like...
//spawns the enemies.
public void LoadEnemies()
{
int randY = random.Next(100, 500);
if (spawn > 1)
{
spawn = 0;
if (enemies.Count() < 4)
enemies.Add(new Enemies(Content.Load<Texture2D>("meteor"), new Vector2(1110, randY)));
}
//Here's where the error lies because of the bulletcolliding (I think)
for (int i = 0; i < enemies.Count; i++)
{
if (!enemies[i].isVisible || enemies[i].bulletColliding)
{
enemies[i].bulletColliding = false;
enemies.RemoveAt(i);
i--;
}
}
}
By strictly having a single bulletColliding variable, what is happening is when any meteor is hit, then it is set to true. So when your method checks the meteors sequentially it sees that bulletColliding is set to true even when it checks the first object, and the code inside the if runs. Using enemy specific booleans will let you know which enemy got hit, and will be removed accordingly.
Related
a and objeler [] are gameobjects.I use yoketme 5 times.I get some random objects with a and give them gravity.But only 1 of the objects lose gravity after they get in bekleme.The last a is the one overwriten to all a objects that uses yoketme before timer(9 seconds).How can i make bekleme to take all gameobjects and not the last one only?
void YokEtme(){
int x = Random.Range (35, 0);
a = objeler [x];
z = a.GetComponent<Transform> ().position;
a.GetComponent<Rigidbody> ().useGravity = true;
Debug.Log (a);
StartCoroutine (bekleme ());
}
IEnumerator bekleme (){
yield return new WaitForSeconds (9);
a.GetComponent<Rigidbody> ().useGravity = false;
a.GetComponent<Rigidbody> ().constraints = RigidbodyConstraints.FreezePosition;
a.transform.position= z;
a.GetComponent<Rigidbody> ().constraints = RigidbodyConstraints.None;
Debug.Log (a);
}
Since some contexts are missing from the question, please allow me to boldly assume that:
The provided code are part of a MonoBehaviour.
a and z are fields/properties of the MonoBehaviour.
What you want to do is to randomly pick five gameobjects from objeler, apply gravity to them, and unapply gravity after a few seconds.
If my assumptions are correct, the problem is pretty simple. The reason that only one chosen object loses gravity is because that Unity allows only one coroutine being executed at a time.
That is, even though you called YokEtme five times and each will lead to StartCoroutine(bekleme()) and expected that there will be five coroutine running in parallel, actually each call to StartCoroutine(bekleme()) will discard the previously running coroutine and start a new one.
So a possible solution is to handle all the five gameobjects in one coroutine instance. Such as:
using System.Collections.Generic;
using UnityEngine;
public class SomeMono : MonoBehaviour
{
private List<GameObject> _controlledGOs = new List<GameObject>();
private List<float> _loggedZs = new List<float>();
void YokEtme()
{
int x = Random.Range(35, 0);
a = objeler [x];
a.GetComponent<Rigidbody>().useGravity = true;
_controlledGOs.Add(a);
_loggedZs.Add(a.GetComponent<Transform>().position);
}
void SetGravityForGameObjects()
{
for (var i = 0; i < 5; i++)
YokEtme();
StartCoroutine()
}
IEnumerator bekleme ()
{
yield return new WaitForSeconds (9);
for (var i = 0; i < _controlledGOs.Count; i++)
{
var a = _controlledGOs[a];
a.GetComponent<Rigidbody>().useGravity = false;
a.GetComponent<Rigidbody>().constraints = RigidbodyConstraints.FreezePosition;
a.transform.position= _loggedZs[i];
a.GetComponent<Rigidbody>().constraints = RigidbodyConstraints.None;
Debug.Log (a);
}
}
}
I'm working on a prototype of match puzzle game. I'm using Unity. Main mechanic is very simple: grid 7x8 filled with tiles of 5 kinds. When user tap on any tile, it looks for similar adjacent tiles and if there is at least 3 of a kind, then they are removed.
Tiles are regular GameObjects with a SpriteRenderer component and my script which defines what sprite to load. Also it handles click but all other logic runs on the Grid.
So, I've already done: grid initialization, detecting same tiles and removing them. I'm doing it like that:
void RemoveShape() {
foreach (DashItem item in items) {
if (item.shouldBeDestroyed) {
item.GetComponent<SpriteRenderer> ().sprite = null;
item.shouldBeDestroyed = false;
item.alreadyChecked = false;
}
}
StopCoroutine (FindNullTiles ());
StartCoroutine (FindNullTiles ());
}
As you can see I don't destroy actual GameObject, just setting it sprite to null.
public IEnumerator FindNullTiles() {
for (int x = 0; x < gridWidth; x++) {
for (int y = 0; y < gridHeight; y++) {
if (items[x, y].GetComponent<SpriteRenderer>().sprite == null) {
yield return StartCoroutine(ShiftTilesDown(x, y));
break;
}
}
}
}
And now I don't know how to implement ShiftTilesDown().
This method should run through all tiles, check if they sprites == null and shift down to fill the gaps. Then I need to fill top empty sprites.
Thanks
Y'all remember that game where you had a wooden board and tried to get the ball through the maze w/o avoiding pitfalls? I am making a clone of that (except on the six sides of a cube). Instead of pitfalls, I added bombs that players have to actively disarm before they can progress. I recently added some code to make it more user friendly, but I keep on getting the error:
UnityException: You are not allowed to call this function when declaring a variable. Move it to the line after without a variable declaration. If you are using C# don't use this function in the constructor or field initializers, Instead move initialization to the Awake or Start function. UnityEngine.GameObject..ctor () (at C:/buildslave/unity/build/artifacts/generated/common/runtime/UnityEngineGameObject.gen.cs:419) RandoLetter..cctor () Rethrow as TypeInitializationException: An exception was thrown by the type initializer for RandoLetter
RandoLetter is the script that creates the code for the player to destroy the bomb, but its acting up for some reason. Here is the code:
using UnityEngine; using System.Collections; using UnityEngine.UI; using System.Collections.Generic;
public class RandoLetter : MonoBehaviour { //The array list of bombs public static GameObject Bombs = new GameObject();
//Array List of Particles
public static GameObject Particles = new GameObject();
//The list of colliders on the bombs
public static Collider Triggers = new Collider();
//The symbol that shows up
int LetterID;
//The speed at which a new symbol appears
int counter, CounterMax;
//The number of symbols that appear in one instance
int LetterCount;
//Which slot in the array that the player is accessing
public int entryID;
//The symbols that appear
public GameObject[] Arrows = new GameObject[4];
//The arrow buttons
public GameObject ArrowBN;
public static GameObject ArrowBTN;
//The randomly generated code
int[] PassCode = new int[4];
//The player game object to be destroyed
public GameObject player;
//The buttons that let the player rotate
public GameObject MoveUI;
//The particle effect when the player dies
public GameObject death;
//The timer before the player or bomb expires
int DeathCounter;
//The code that the player puts in
int[] YourCode = new int[4];
int CounterToMax = 0;
//Is the bomb currently being disarmed?
public static bool disarming = false;
void RestoreStart()
{
//How much longer until the next symbol appears
CounterToMax = 0;
//Is the player attempting to disarm the bomb
disarming = false;
//How many symbols show up
LetterCount = 4;
//Which slot in the array is the player accessing
entryID = 0;
//The UI that lets the player punch in part of the code
BallMoving.BombU.SetActive(false);
//Disabling the previous UI for the code
SetUIFalse();
}
// Use this for initialization
void Start()
{
//The timer until the player expires after failing to disarm a bomb
DeathCounter = 1000;
//The slot in the array that the player is accessing
entryID = 0;
//The amount of characters in the code being presented to the player
LetterCount = 4;
//Setting the static value to the public value
ArrowBTN = ArrowBN;
//The time between arrows
counter = 50;
//Setting CounterMax (the starting value of the counter) to the amount of time represented by counter
CounterMax = counter;
}
// Update is called once per frame
void Update()
{
//If the player is attempting to disarm the bomb
if (disarming)
{
//decrementing counter
counter--;
//Disabling the UI that allows the player to move
MoveUI.active = false;
//Special code for the last letter in the sequence
if (CounterToMax == LetterCount && counter == 0)
{
Arrows[LetterID].active = false;
}
//In the first 3 letters
if (counter == 5 && CounterToMax != LetterCount)
{
Arrows[LetterID].active = false;
}
//If the counter goes lower than zero somehow (just a catch in case the code above malfunctions)
if (counter < 0)
{
counter = CounterMax;
}
//Randomly Generating the letter
if (counter == 0 && CounterToMax != LetterCount)
{
LetterID = Random.Range(1, 4);
counter = CounterMax;
#region AssignLetter
//Assigning the letter to the value in the array slot
Arrows[LetterID].active = true;
PassCode[CounterToMax] = LetterID;
CounterToMax++;
#endregion
}
}
//Re-enabling the move UI
if (!disarming)
{
MoveUI.active = true;
}
//Enabling the UI that allows the player to punch in the code
if (CounterToMax == LetterCount && disarming == true)
{
ArrowBTN.active = true;
}
}
//Enabling the player to put in a code based off of a UI element
public void AddDirection(int number)
{
YourCode[entryID] = number;
entryID++;
//If the player is at the last letter, make sure that the player's code matches the randomly generated one
if (entryID == LetterCount)
{
//Resetting the entryID (slot in the array) for the next time a player defuses a bomb
entryID = 0;
CheckCorrect();
}
}
//Checking to make sure that the player entered in the correct code
public void CheckCorrect()
{
for (int i = 0; i < LetterCount; i++)
{
//If the player failed......
if (YourCode[i] != PassCode[i])
{
//Destroy the player
DestroyPlayer();
}
}
//Otherwise destroy the bomb
DestroyBomb();
}
//Re-enabling the player's UI that allows them to move
public void SetUIFalse()
{
disarming = false;
ArrowBTN.active = false;
}
//Destroying the player
public void DestroyPlayer()
{
print("Player Destroyed!");
SetUIFalse();
for (int i = 0; i < DeathCounter; i++)
{
Destroy(player);
if (i == DeathCounter - 1)
{
//Switching the levels after the player dies
switch (RotateBlock.level)
{
case 1:
Application.LoadLevel("Level1");
break;
case 2:
Application.LoadLevel("Level2");
break;
}
}
}
}
//Destroying the bomb
public void DestroyBomb()
{
//Resetting everything for the next time the player defuses a bomb
RestoreStart();
print("In DestroyBomb");
for (int i = 0; i < DeathCounter; i++)
{
if (i == DeathCounter - 1)
{
//Allowing the player to move again
Rigidbody rb;
rb = player.GetComponent<Rigidbody>();
rb.isKinematic = false;
//Disabling the bomb
Bombs.SetActive(false);
//Enabling the particle effect
Particles.active = true;
//Destroying the collider on the bomb
Triggers.enabled = false;
}
}
}
}
Sorry if it looks like a bunch of junk, I tried to comment it as best as I could. Anyways, it has been bugging me for a while and I really appreciate any help :D
You have to create a new instance of GameObject inside a function.
Replace public static GameObject Particles = new GameObject();
with
public static GameObject Particles;
void Awake()
{
Particles = new GameObject();
}
I'm having a problem pooling some objects. I've looked at a starter file we got in class to recreate the scene but different, but I'm not having the correct results and I can't seem to figure it out :/
What I have is:
Environment which has 3 child object pools (ground, dew, corona). Ground has 3 childs (planes), dew and corona both have 10 childs (models).
What I'm trying to do is move the first object in an array to the last place of the array and changing the z position to move it forward once the player has ran past it.
My ground objects correctly go forward once my character has passed a certain position, the dews and coronas however do not, and I don't understand why.
This is the code I'm using:
public class Environment : MonoBehaviour
{
List<Transform> dews = new List<Transform>();
List<Transform> coronas = new List<Transform>();
List<Transform> grounds = new List<Transform>();
Transform player;
int propsCount = 0;
int groundCount = 0;
// Use this for initialization
void Start()
{
player = GameObject.Find("Player").transform;
dews = GameObject.Find("DewPool").GetComponentsInChildren<Transform>().ToList();
coronas = GameObject.Find("CoronaPool").GetComponentsInChildren<Transform>().ToList();
grounds = GameObject.Find("GroundPool").GetComponentsInChildren<Transform>().ToList();
dews.RemoveAt (0);
coronas.RemoveAt (0);
grounds.RemoveAt (0);
}
// Update is called once per frame
void Update()
{
// Voeg hier code toe voor Object Pooling!
Vector3 nieuwePos;
if (player.transform.position.z > dews[0].transform.position.z + 5)
{
dews[0].transform.position = nieuwePos = new Vector3(dews[0].transform.position.x, dews[0].transform.position.y, dews[0].transform.position.z + 50);
dews.Add(dews[0]);
dews.RemoveAt(0);
}
if (player.transform.position.z > coronas[0].transform.position.z + 5)
{
coronas[0].transform.position = nieuwePos = new Vector3(coronas[0].transform.position.x, coronas[0].transform.position.y, coronas[0].transform.position.z + 55);
coronas.Add(coronas[0]);
coronas.RemoveAt(0);
}
if (player.transform.position.z > grounds[0].transform.position.z + 40)
{
grounds[0].transform.position = nieuwePos = new Vector3(grounds[0].transform.position.x, grounds[0].transform.position.y, grounds[0].transform.position.z + 120);
grounds.Add(grounds[0]);
grounds.RemoveAt(0);
}
}
}
All the pools containing the objects are positioned at 0, the dews and coronas start at Z-index 7.5 and 10 respectively and the copies are always 5 Z further.
Player is an empty gameobject positioned at 0 - 0 - 4.4 containing the model Ethan.
The first dew and corona correctly get pushed backwards, the rest is very buggy.
If anyone can help out it would be greatly appreciated, thanks in advance!
Fixed it by adding
for (int i = dews.Count-1; i > 0; i-=2) {
dews.RemoveAt (i);
}
for (int i = coronas.Count-1; i > 0; i-=2) {
coronas.RemoveAt (i);
}
into my start() function, my objects had another child while my ground objects did not.
Probably not the best solution but for now I'm okay with this :-)
How to make my bullet goes form start of X axis to y axis randomized by +3 -3 pixels
public void UpdateBullets()
{
//For each bullet in our bulletlist: Update the movment and if the bulet hits side of the screen remove it form the list
foreach(Bullet bullet in bulletList)
{
//set movment for bullet
bullet.position.X = bullet.position.X + bullet.speed;
//if bullet hits side of screen, then make it visible to false
if (bullet.position.X >= 800)
bullet.isVisible = false;
}
//Go thru bulletList and see if any of the bullets are not visible, if they aren't then remove bullet form bullet list
for (int i = 0; i < bulletList.Count; i++)
{
if(!bulletList[i].isVisible)
{
bulletList.RemoveAt(i);
i--;
}
}
}
When i hold space bullets just go forwards and i want it to strafe little by Y axis as well.
Here is http://www.afrc.af.mil/shared/media/photodb/photos/070619-F-3849K-041.JPG to what i want to do.
Never to hit only in 1 spot. Just to randomize Y axis a bit.
What i want to change is
//set movment for bullet
bullet.position.X = bullet.position.X + bullet.speed;
to something like
BUllet.position.X = Bullet.position.X + Bullet.random.next(-3,3).Y + bullet.speed.
Something like that.
You need to define a constant value for each bullet representing the change in the y axis over time:
Change your Bullet class
public Class Bullet
{
// ....
public int Ychange;
System.Random rand;
public Bullet()
{
// ....
rand = new Random();
Ychange = rand.Next(-3, 3);
}
// Add the above stuff to your class and constructor
}
New UpdateBullets Method
public void UpdateBullets()
{
//For each bullet in our bulletlist: Update the movment and if the bulet hits side of the screen remove it form the list
foreach(Bullet bullet in bulletList)
{
//set movment for bullet
bullet.position.X += bullet.speed;
bullet.position.Y += bullet.Ychange;
// Above will change at a constant value
//if bullet hits side of screen, then make it visible to false
if (bullet.position.X >= 800)
bullet.isVisible = false;
}
//Go thru bulletList and see if any of the bullets are not visible, if they aren't then remove bullet form bullet list
for (int i = 0; i < bulletList.Count; i++)
{
if(!bulletList[i].isVisible)
{
bulletList.RemoveAt(i);
i--;
}
}
}
And that should work.