Recursive method calling freezes Unity - c#

I'm trying to call a method CreateFinalPath() using recursion but Unity freezes the second time I call the methode Test().
Do you have any idea what the problem could be?
I nearly tried everything , no clue what is going on.
public class SplinePlus : MonoBehaviour
{
int h = 0;
public void Test()
{
FinalPath.Vertices.Clear();
FinalPath.Tangents.Clear();
FinalPath.Normals.Clear();
CreateFinalPath();
}
private void CreateFinalPath()
{
for (int i = 0; i < Branches[h].Vertices.Count; i++)// branch vertices
{
for (int n = 0; n < SharedNodes.Count; n++) // shared nodes loop
{
if (SharedNodes[n].Nodes.Point.position == Branches[h].Vertices[i]) // shared node detected , see next branch to take
{
h = SharedNodes[n].BranchToFollow; // next branch ID to take
CreateFinalPath(); // trigger data storage for the new branch in Final path class
break;
}
}
// data storage in finalpath class
FinalPath.Vertices.Add(Branches[h].Vertices[i]);
FinalPath.Normals.Add(Branches[h].Normals[i]);
FinalPath.Tangents.Add(Branches[h].Tangents[i]);
}
}
}
public class FinalPath
{
public List<Vector3> Vertices = new List<Vector3>();
public List<Vector3> Tangents = new List<Vector3>();
public List<Vector3> Normals = new List<Vector3>();
}
public class Branches
{
public List<Vector3> Vertices = new List<Vector3>();
public List<Vector3> Tangents = new List<Vector3>();
public List<Vector3> Normals = new List<Vector3>();
public List<PathPoint> PathPoints = new List<PathPoint>();
}
[System.Serializable]
public class SharedNodesData
{
public PathPoint Nodes = new PathPoint();
public List<string> ConnectedBranches = new List<string>();
public int BranchToFollow = 0;
}
Update2
So what I'm trying to do is to create a final path for a gameObject follower based on a complex path system where there is a main branch (red) and the children branches (yellow) which can be in unlimited number.
The shared nodes are the points with numbers on top of them, I'm looping through the main branch first looking for a shared node, once I find one, I can get the next branch index to take which is defined by the user in an the inspector. I affect that to "h" and call CreateFinalPath() to do the same for the next branch until I get the final complete path.

I didt understand much your codes but maybe you need some things like this
public class Branches
{
public List<Vector3> Vertices = new List<Vector3>();
public List<Vector3> Tangents = new List<Vector3>();
public List<Vector3> Normals = new List<Vector3>();
public List<PathPoint> PathPoints = new List<PathPoint>();
public bool visited = false;
}
private void CreateFinalPath()
{
for (int i = 0; i < Branches[h].Vertices.Count; i++)// branch vertices
{
for (int n = 0; n < SharedNodes.Count; n++) // shared nodes loop
{
if (!Branches[h].visited && SharedNodes[n].Nodes.Point.position == Branches[h].Vertices[i]) // shared node detected , see next branch to take
{
Branches[h].visited = true;
h = SharedNodes[n].BranchToFollow; // next branch ID to take
CreateFinalPath(); // trigger data storage for the new branch in Final path class
break;
}
}
// data storage in finalpath class
FinalPath.Vertices.Add(Branches[h].Vertices[i]);
FinalPath.Normals.Add(Branches[h].Normals[i]);
FinalPath.Tangents.Add(Branches[h].Tangents[i]);
}
}

Related

Synchronize main loop (Spawner.cs) with subloop on Prefab

I'm working on Unity3d project where the floor has to unfold gradually. I created a script FloorModule.cs where using coroutine the floor tiles are laying out gradually. Each next module has to unfold right after previous is completed. There for I created Spawner.cs to loop a new FloorModule.cs right after previous one is completed.
I can't seem to get my head around how to use coroutine to synchronize the mainloop (Spawner.cs) with subloop on prefab (FloorModule.cs).
Here is the link to the example
https://1drv.ms/u/s!AkVZpIE6f1GV4M5Ju7G5zPOrQcCe8w?e=QrghRT
P.S.
In given example, as loop goes forward I'm using "Reference.cs" class to change some variable values .
FloorModule.cs
public class FloorModule : MonoBehaviour
{
public float zSpacer = 0f;
public int instPrefabCount;
public Transform spawnPoint;
public int lenght = 15;
public int width = 5;
public GameObject floorTiles;
void Start()
{
spawnPoint = GetComponent<Transform>();
StartCoroutine(FwFloorDelay(spawnPoint));
}
public IEnumerator FwFloorDelay(Transform origin)
{
for (int l = 0; l < lenght; l++)
{
float xAngle = 90;
float yPos = 0;
float zPos = 0 + l;
for (int w = 0; w < width; w++)
{
int xSelection = Random.Range(0, 6);
GameObject xFloor = Instantiate(floorTiles, origin);
TileStatusNames(xFloor, l, w);
// defining positiona and angles
float xPos = w + (zSpacer * w);
xFloor.transform.localEulerAngles = new Vector3(xAngle, 0, 0);
xFloor.transform.localPosition = new Vector3(xPos, yPos, zPos);
yield return new WaitForSeconds(.05f);
}
}
Spawner.cs
public class Spawner : MonoBehaviour
{
public GameObject FloorModPrefab;
public References[] referenceScript;
void Start()
{
StartCoroutine(SpawnModules());
}
IEnumerator SpawnModules()
{
for (int i = 0; i < referenceScript.Length; i++)
{
referenceScript[i].instance =
Instantiate(FloorModPrefab, referenceScript[i].ref_spawnPoint.position, referenceScript[i].ref_spawnPoint.rotation);
referenceScript[i].ref_instFloorModCount = i + 1;
referenceScript[i].Setup();
yield return new WaitForSeconds(5f);
}
}
}
References.cs
[Serializable]
public class References
{
FloorModule prefabObjScript;
public GameObject instance;
public int ref_instFloorModCount;
public Transform ref_spawnPoint;
public int ref_Width = 5;
public int ref_Lenght = 15;
public void Setup()
{
// Get references to the components.
prefabObjScript = instance.GetComponent<FloorModule>();
// Set the player numbers to be consistent across the scripts.
prefabObjScript.instPrefabCount = ref_instFloorModCount;
prefabObjScript.spawnPoint = ref_spawnPoint;
prefabObjScript.width = ref_Width;
prefabObjScript.lenght = ref_Lenght;
}
}
I tried to use coroutines unfortunately in given context I realize it's impossible for me to resolve this task.
You can yield a coroutine from within another coroutine.
Changes to your Code
In References change public GameObject instance; to public FloorModule instance;
In Spawner change public GameObject FloorModPrefab; to public FloorModule FloorModPrefab;
Remove the code from Start of FloorModule.
Modify FwFloorDelay to
public IEnumerator FwFloorDelay(Transform origin = null)
{
if (origin == null)
{
origin = transform;
}
...
}
In SpawnModules, chain the floor delay coroutine
IEnumerator SpawnModules()
{
for (int i = 0; i < referenceScript.Length; i++)
{
...
yield return referenceScript[i].instance.FwFloorDelay();
}
}
Your goal should not be to synchronize separate coroutines, but rather to get the code running sequentially in one coroutine.
For example, you could make References.Setup() asynchronous, and then have it invoke FloorModel.FwFloorDelay directly instead of the FloorModel starting its own separate coroutine.

UI development and data binding in unity 3d with hololens 2.0 emulator

hi I am new to unity3d development with hololens 2
I want to display employee details in the tile which is interactable
see the sample screenshot below.
AS of now, I created a prefab like a custom component but facing issues in data binding.
I have one empty game object on which dynamically instantiating prefab using c# script
I have prefab of employee tile as below
As you can see I have two fields image (emp image) and text (emp name)
A script which is bound to empty game object
public class CreateEmployeeInfoTiles : MonoBehaviour
{
private TextMeshProUGUI textMesh;
private string _employeeName;
private Employee[] emp;
public GameObject employeeTile;
// Start is called before the first frame update
void Start()
{
Employee empobj = new Employee();
emp = empobj.GenrateEmployees(6).ToArray();
employeeTile = Resources.Load("EmployeeTile") as GameObject;
int j = 10;
for (int i = 0; i < emp.Length; i++)
{
var empTi = Instantiate(employeeTile, new Vector3((i * 10), (j * 20), 115), Quaternion.identity) as GameObject;
empTi.SendMessage("BindData", emp[i]);
j =+ 30;
}
}
// Update is called once per frame
void Update()
{
}
}
Employee class as a DTO
public class Employee
{
public string Name { get; set; }
public string Job { get; set; }
public string imageUrl { get; set;}
public List<Employee> GenrateEmployees(int noOfEmployees)
{
var emp = new List<Employee>();
for (int i = 0; i < noOfEmployees; i++)
{
emp.Add(new Employee() { Name = RandomName(), Job = RandomName(),
imageUrl = "https://url/image/placeimg.jpg"});
}
return emp;
}
}
I want to set employee data to respective game object/UI fields in dynamically generated employee tile prefab.
Thanks in advance.
First it seems very strange to me that GenerateEmployees should be an instanced method. You are creating a new Employee just to then generate more. This should rather be a static method I guess!
[Serializable]
public class Employee
{
public string Name;
public string Job;
public string ImageUrl;
// RandomName will also have to be static
public static List<Employee> GenrateEmployees(int noOfEmployees)
{
// Lists in c# grow dynamically bigger.
// It is more efficient to already set the final size
var emp = new List<Employee>(i);
for (int i = 0; i < noOfEmployees; i++)
{
emp.Add(new Employee()
{
Name = RandomName(),
Job = RandomName(),
imageUrl = "https://url/image/placeimg.jpg"
}
);
}
return emp;
}
}
Then there should simply be a certain component (script) attached to the employee prefab like e.g.
public class EmployeeController : MonoBehaviour
{
// These you reference via the Inspector in the prefab
[SerializeField] private Image image;
[SerializeField] private TextMeshProUGUI nameField;
[SerializeField] private TextMeshProUGUI designationField;
// And finally have a method you can call directly
// Using SendMessage is very inefficient and unsecure
public void BindData(Employee data)
{
nameField.text = data.Name;
designationField.text = data.Job;
// Start downloading the image
StartCoroutine(DownloadImage(data.ImageUrl));
}
private IEnumerator DownloadImage(string url)
{
using (UnityWebRequest www = UnityWebRequestTexture.GetTexture(url))
{
yield return www.SendWebRequest();
if (www.isNetworkError || uwr.isHttpError)
{
Debug.Log(www.error);
}
else
{
// Get downloaded texture
var texture = DownloadHandlerTexture.GetContent(www);
// Create a sprite from the texture
var sprite = Sprite.Create(texture, new Rect(0,0, texture.width, texture.height), Vector2.one * 0.5f);
// Assign it to the image
image.sprite = sprite;
}
}
}
}
And finally you would simply use the static method to create Employee data and then use GetComponent to retrieve a reference to the EmployeeController component on the instantiated prefab. Even better would be not using Resources at all! Rather put the prefab in a normal folder and directly drag&drop it into the prefab field of CrrateEmployeeInfoTiles
public class CreateEmployeeInfoTiles : MonoBehaviour
{
// NOTE: Do NOT use Resources!
// rather place your prefab in any other folder and then simply drag it in here
// via the Inspector!
// By using directly the correct type instead of GameObject you
// a) do not need the GetComponent later since Instantiate already returns the dsme type
// b) have more security since now you only can drag&drop a GameObject here
// that actually has the required component attached
[SerializeField] private EmployeeController employeePrefab;
// Start is called before the first frame update
private void Start()
{
var employeeData = Employee.GenrateEmployees(6).ToArray();
var j = 10;
for (int i = 0; i < employeeData.Length; i++)
{
// Since employeePrefab is of type EmployeeController
// Instantiate already returns the component reference
var employee = Instantiate(employeePrefab, new Vector3((i * 10), (j * 20), 115), Quaternion.identity);
employee.BindData(emp[i]);
j =+ 30;
}
}
}
Typed on smartphone but I hope the idea gets clear

C# Updating a value in a list of objects is updating other values too

I was working on a simple turn based RPG game. I was working on a battle system. I had it working with 1 enemy, and wanted to update it to work on multiple enemies. I have it spawning multiple enemies, but I am having issues targeting them.
Basically, the enemies are stored in a list called actualEnemies. The enemies are generated at run time, and there can be between 1-3. A label is generated to display the enemy name, and the enemy is selected by double clicking on it.
The issue seems to be in the naming of the enemy's. They have a property called characterName to identify an enemy. The problem is, there can be 2 or more of the same type of character (for example, 3x Goblin). So in the code where the enemies are generated, I was wanting to add a 1 to the characterName if there already is one in the list. The idea is to give each enemy a unique name.
The problem is, when I try this, it changes the values of all of the enemies of the same name, and this is screwing with everything. Here is the relevant section of code:
if (EnemyExists(e)) {
e.characterName += c.ToString();
}
c is simply a counter that is being used, to let me know if it is enemy 1, 2 or 3.
The EnemyExists function is as follows:
public bool EnemyExists(Enemy e) {
bool exists = false;
int eCount = 0;
foreach (Enemy en in actualEnemies) {
if (en.characterName == e.characterName) {
eCount++;
}
}
if (eCount > 1) {
exists = true;
}
return exists;
}
Just to note, the enemies all start off with a default name. For this example I am working on, there are only 2 kinds of enemy. The game picks a random number between 1-3, to decide how many enemies there are. Each enemy will be one of the 2 pre defined enemies, picked at random again. This is why I just want to add a number to the end of a name that is already present.
I suspect it has something to do with how I am creating the list of enemies:
public void CreateActualEnemies() {
int r;
int potEnemyNum = potentialEnemies.Count;
int numEnemies;
Random rand = new Random();
numEnemies = rand.Next(1, 4 );
for (int i = 0; i < numEnemies; i++) {
r = rand.Next(0,potEnemyNum);
Enemy ene = new Enemy();
ene = potentialEnemies.ElementAt(r);
actualEnemies.Add(ene);
}
DisplayEnemyDetails();
}
When I add to actualEnemies, am I just pointing to an address in potentialEnemies, so even though I am instantiating a new Enemy, each one, if it has the same name, points to the same place? Do I need to properly copy the object?
It is late, I am tired, but I feel like I am getting closer, any help would be great, thanks.
EDIT:
Enemy Class:
public class Enemy:Character {
public int xpGiven { get; set; }
public LootTable lootTable { get; set; }
public int goldDropped { get; set; }
public bool alive { get; set; }
public NamedTimer timer { get; set; }
public Enemy() {
alive = true;
xpGiven = 10;
lootTable = new LootTable();
goldDropped = 10;
timer = new NamedTimer();
}
}
Breaking down your question:
When I add to actualEnemies, am I just pointing to an address in potentialEnemies, so even though I am instantiating a new Enemy, each one, if it has the same name, points to the same place?
By doing
Enemy ene = new Enemy();
You are essentialy creating a new pointer in memory with a "blank" valued-reference.
However, since you did
ene = potentialEnemies.ElementAt(r);
You overwrote it with the reference to the element in the list. So whatever changes you do the ene object will reflect the item in the list.
If you want to segregate said changes, try using a MemberwiseClone.
From the example given at MSDN:
public class Person
{
public int Age;
public string Name;
public IdInfo IdInfo;
public Person ShallowCopy()
{
return (Person) this.MemberwiseClone();
}
public Person DeepCopy()
{
Person other = (Person) this.MemberwiseClone();
other.IdInfo = new IdInfo(IdInfo.IdNumber);
other.Name = String.Copy(Name);
return other;
}
}
1: Property names should start with a capital letter in C#. I.e. public int xpGiven { get; set; } should be public int XpGiven { get; set; }. If it's a class variable (no get or set defined), you start with a lower case letter. To more easily identify class variables from local variables, programmers typically start them with a _, although this is not mandatory.
2: When doing ==, please be aware enemyA == enemya will return false. Use enemyA.Equals(enemya, StringComparison.OrdinalIgnoreCase) to ignore capital differences.
3:
public void CreateActualEnemies()
{
int r;
int potEnemyNum = potentialEnemies.Count;
int numEnemies;
Random rand = new Random();
numEnemies = rand.Next(1, 4 );
for (int i = 0; i < numEnemies; i++)
{
r = rand.Next(0,potEnemyNum);
Enemy ene = new Enemy();
ene = potentialEnemies.ElementAt(r);
actualEnemies.Add(ene);
}
DisplayEnemyDetails();
}
should be:
public void CreateActualEnemies()
{
Random rand = new Random();
int numEnemies = rand.Next(1, 4 );
for (int i = 0; i < numEnemies; i++)
{
Enemy ene = new Enemy();
ene.characterName = "" + ene.GetType().Name + " " + i; // or something similar to give the ene a unique name.
actualEnemies.Add(ene);
}
DisplayEnemyDetails();
}

How can i get a specific GameObjects to array without using FindGameObjectsWithTag?

In the first script i clone some GameObjects:
using System;
using UnityEngine;
using Random = UnityEngine.Random;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
public class CloneObjects : MonoBehaviour
{
public GameObject ObjectToCreate;
public int objectsHeight = 3;
[HideInInspector]
public GameObject[] objects;
// for tracking properties change
private Vector3 _extents;
private int _objectCount;
private float _objectSize;
private List<GameObject> cloneList = new List<GameObject>();
/// <summary>
/// How far to place spheres randomly.
/// </summary>
public Vector3 Extents;
/// <summary>
/// How many spheres wanted.
/// </summary>
public int ObjectCount;
public float ObjectSize;
public static float LargestSize = 0;
// Use this for initialization
void Start()
{
Clone();
//objects = GameObject.FindGameObjectsWithTag("ClonedObject");
objects = cloneList.ToArray();
foreach (var element in objects)
{
float Size = element.transform.localScale.x;
if (Size > LargestSize)
LargestSize = Size;
}
}
private void OnValidate()
{
// prevent wrong values to be entered
Extents = new Vector3(Mathf.Max(0.0f, Extents.x), Mathf.Max(0.0f, Extents.y), Mathf.Max(0.0f, Extents.z));
ObjectCount = Mathf.Max(0, ObjectCount);
ObjectSize = Mathf.Max(0.0f, ObjectSize);
}
private void Reset()
{
Extents = new Vector3(250.0f, 20.0f, 250.0f);
ObjectCount = 100;
ObjectSize = 20.0f;
}
// Update is called once per frame
void Update()
{
}
private void Clone()
{
if (Extents == _extents && ObjectCount == _objectCount && Mathf.Approximately(ObjectSize, _objectSize))
return;
// cleanup
//var ObjectsToDestroy = GameObject.FindGameObjectsWithTag("ClonedObject");
var ObjectsToDestroy = objects;
foreach (var t in ObjectsToDestroy)
{
if (Application.isEditor)
{
DestroyImmediate(t);
}
else
{
Destroy(t);
}
}
var withTag = GameObject.FindWithTag("Terrain");
if (withTag == null)
throw new InvalidOperationException("Terrain not found");
for (var i = 0; i < ObjectCount; i++)
{
var o = Instantiate(ObjectToCreate);
cloneList.Add(o);
o.transform.SetParent(base.gameObject.transform);
o.transform.localScale = new Vector3(ObjectSize, ObjectSize, ObjectSize);
// get random position
var x = Random.Range(-Extents.x, Extents.x);
var y = Extents.y; // sphere altitude relative to terrain below
var z = Random.Range(-Extents.z, Extents.z);
// now send a ray down terrain to adjust Y according terrain below
var height = 10000.0f; // should be higher than highest terrain altitude
var origin = new Vector3(x, height, z);
var ray = new Ray(origin, Vector3.down);
RaycastHit hit;
var maxDistance = 20000.0f;
var nameToLayer = LayerMask.GetMask("Terrain");
var layerMask = 1 << nameToLayer;
if (Physics.Raycast(ray, out hit, maxDistance, layerMask))
{
var distance = hit.distance;
y = height - distance + y; // adjust
}
else
{
Debug.LogWarning("Terrain not hit, using default height !");
}
// place !
o.transform.position = new Vector3(x, y + objectsHeight, z);
}
_extents = Extents;
_objectCount = ObjectCount;
_objectSize = ObjectSize;
}
}
Before in the first time i used to give the children objects i clone a Tag name.
So i could do:
var ObjectsToDestroy = GameObject.FindGameObjectsWithTag("ClonedObject");
But now i'm not using this line. So all the cloned objects are untagged.
So now i'm not using this line.
But now i want to get all the cloned objects from another script:
void Start()
{
anims = GetComponent<Animations>();
waypoints = GameObject.FindGameObjectsWithTag("ClonedObject");
originalPosition = transform.position;
}
But since the cloned onjects are without any tag waypoints will be empty.
waypoints is array of GameObject: GameObject[] waypoints.
Should i give tag name to every cloned object ? Or is there any other way to get all the cloned objects into array/List ?
Solution A
Make your cloneList public or create a public property that returns it, then access it from another script.
public class CloneObjects : MonoBehaviour
{
public List<GameObject> cloneList = new List<GameObject>();
}
public class AnotherScript : MonoBehaviour
{
void Start()
{
var cloneObjects = obj_with_CloneObjects.GetComponent<CloneObjects>();
//cloneObjects.cloneList
}
}
Solution B
Create a field of type List<GameObject> in another script. After cloning, assign the cloneList to that field.
public class CloneObjects : MonoBehaviour
{
public List<GameObject> cloneList = new List<GameObject>();
void Clone()
{
}
void Start()
{
Clone();
obj_with_AnotherScript.GetComponent<AnotherScript>.cloneList = cloneList;
}
}
public class AnotherScript : MonoBehaviour
{
public List<GameObject> cloneList = new List<GameObject>();
void Start()
{
//this.cloneList
}
}
But since the cloned onjects are without any tag waypoints will be
empty. waypoints is array of GameObject: GameObject[] waypoints.
You can change the tag of the GameObject after instantiating it.
var o = Instantiate(ObjectToCreate);
o.tag = "ClonedObject";
Just make sure that you create a tag called "ClonedObject" in the
Editor. Now, GameObject.FindGameObjectsWithTag("ClonedObject"); should return something if there are instantiated Objects with that tag name.
Should I give tag name to every cloned object ? Or is there any other
way to get all the cloned objects into array/List ?
Using tag with FindGameObjectsWithTag should do fine. If you care about performance then then use List since FindGameObjectsWithTag will search for for GameObjects with that tag. It is is slower.
I noticed that you are already storing instantiated GameObjects in a List.
Change
private List<GameObject> cloneList = new List<GameObject>();
to
public List<GameObject> cloneList = new List<GameObject>();
If there is only one instance of CloneObjects script in your scene, you can use FindObjectOfType to find the CloneObjects script then access the cloneList variable.
CloneObjects cloneObjectsInstance = FindObjectOfType<CloneObjects>();
List<GameObject> clonedObj = cloneObjectsInstance.cloneList;
for (int i = 0; i < clonedObj.Count; i++)
{
}
If there are more than one instance of CloneObjects script in your scene, Find the GameObject that script is attached to then perform GetComponent on it.
CloneObjects cloneObjectsInstance = GameObject.Find("ObjectCloneObjectsIsAttachedTO").GetComponent<CloneObjects>();
List<GameObject> clonedObj = cloneObjectsInstance.cloneList;
for (int i = 0; i < clonedObj.Count; i++)
{
}

Making an instantiated class array with a method

I have a class "Bullet" which I instantiate using a method CreateBullet(), since there are going to be multiple bullets i decided that I should make bullet an array, though this didn't work out and I've spent an hour on trying to fix it.
What I call in my Initialize method:
Bullet bullet[] = Bullet.CreateBullet[1]();
The Bullet class:
class Bullet
{
public float2 position;
public float angle { get; set; }
public float speed { get; set; }
public static Bullet CreateBullet()
{
Bullet bullet = new Bullet()
{
position = new float2()
};
return bullet;
}
public void Move()
{
}
}
Could you please show me what's wrong with the code? Thank you in advance.
With this, you create an array of 5 bullets:
Bullet[] bullets = new Bullet[5];
And then you need to fill the array by creating a bullet for each array entry:
for (int i = 0; i < bullets.Length; i++)
{
bullets[i] = Bullet.CreateBullet();
}
You can wrap this logic in a function:
public Bullet[] CreateBullets(int amount)
{
Bullet[] bullets = new Bullet[amount];
for (int i = 0; i < bullets.Length; i++)
{
bullets[i] = Bullet.CreateBullet();
}
return bullets;
}
And then you can use a function to initialize the array:
public void Test()
{
Bullet[] bullets = CreateBullets(5);
}
You could do something like this, not quite what you where trying to achieve, but it might inspire you a bit more
Usage
// Create your bullets
var bullets = new List<Bullet>();
// Create a raw/empty bullet with default properties
var newBullet1 = new Bullet();
// Create bullet with some initialized properties
var newBullet2 = new Bullet()
{
Angle = 35,
Position = 0,
Speed = 200
};
bullets.Add(newBullet1);
bullets.Add(newBullet2);
Something extra for fun
// Move all your bullets at once
foreach (var bullet in bullets)
{
bullet.Move();
}

Categories

Resources