set gameobject inactive with smallest value - c#

I can't seem to find a answer to this, i believe it's impossible to do by now.
i have a player that collects gameobjects, when the gameobjects are collected they become a child of the player. All gameobjects have a rigidbody with different mass but duplicate gameobjects have the same mass offcourse.
The player grows in mass when gameobjects are collected.
What i want to do is when the player reaches a certain amount of mass and collect another gameobject i want the gameobject with smallest mass to set inactive.
I already got this:
Component[] objects = GetComponentsInChildren(typeof(Rigidbody), false);
foreach (Rigidbody joint in objects)
{
float smallest = Mathf.Min(joint.mass);
if (smallest <= mass / 100 && joint.gameObject.activeInHierarchy)
{
int i = 0;
bool plus = false;
while (i < transform.childCount)
{
if (transform.GetChild(i).gameObject.activeInHierarchy && plus == false)
{
transform.GetChild(i).gameObject.SetActive(false);
Debug.Log("disabled");
plus = true;
}
i++;
}
}
}
This sets the first child gameobject of the player inactive after it reaches a certain amount of mass.
but this isn't what i really want, i want to get the gameobject with the smallest mass to set inactive but i can't convert the "float smallest = Mathf.Min(joint.mass);" back to an gameobject.
Is this possible to achieve or is it just impossible?

Of course it is possible my friend
using System.Linq // < --- at top of script
List<Rigidbody> objects = GetComponentsInChildren<Rigidbody>(false)
.OrderBy(i => i.mass)
.ToList();
Rigidbody smallest = objects.FirstOrDefault();

Related

How can I check whether a gameObject is contained in a list?

I need to check weather a hole is full or empty, by comparing the transform position of both Cylinders and Holes. Note that I can't simply do else if (cylpos != holpos) as I need it to be that way. It also seems that if (!fullHoles.Contains(hole)) doesn't work as I want it to.
foreach (GameObject hole in Holes)
{
holpos = hole.transform.position;
foreach (GameObject cylinder in Cylinders)
{
cylpos = cylinder.transform.position;
if (cylpos == holpos)
{
fullHoles.Add(hole);
}
}
if (!fullHoles.Contains(hole))
{
emptyHoles.Add(hole);
Debug.Log(hole);
}
}
Thanks for your help!
I don't see why fullHoles.Contains should not do the trick here.
But note that the Vector3 == operator uses a precision of 0.00001 for equality meaning it does the same as
if(Vector3.Distance(cylpos, holpos) < 0.00001f)
I don't know how exactly you move the according objects but it is very unlikely that a moved GameObject will exactly match a position with that precision on runtime.
So I can only guess that the objects where not added to fullholes the way you expected.
You should probably rather use
[SerializeField] private float threshold = 0.1f;
...
if(Vector3.Distance(cylpos, holpos) < threshold)
and adjust the threshold via the Inspector according to your needs

Is there a better way to randomize which enemy to spawn?

Im currently making a game where I want to spawn a lot of different enemies. Currently I have a lot Data containers for units I create through ScriptableObjects. These units contain different sorts of data such as HP, loot, damage etc. I've thought of adding another type of data which is going to be their individual % to spawn or something similar but I am kind off stuck not knowing how to implement something like that.
Here is my current code where I spawn enemies completely randomize from two different lists of either Melee or Ranged enemies.
IEnumerator SpawnEnemies()
{
//Spawn enemies while amount of enemies are under predetermined enemy amount.
while (spawnedEnemies < totalEnemiesInCurrentWave)
{
//Randomize if Melee or Ranged enemy.
whichEnemyType = Random.Range(0, 2);
GameObject enemy = waves[currentWave].enemyPrefab[whichEnemyType];
spawnedEnemies++;
enemiesInWaveLeft++;
//Can I get a probability % to spawn each enemy? Currently completely random.
switch (whichEnemyType)
{
case 0:
GameObject Melee = Instantiate(enemy, spawnPoint.position, Quaternion.identity);
Melee.GetComponent<Melee>().unit = meleeUnits[Random.Range(0, meleeUnits.Count)];
break;
case 1:
GameObject Ranged = Instantiate(enemy, spawnPoint.position, Quaternion.identity);
Ranged.GetComponent<Ranged>().unit = rangedUnits[Random.Range(0, rangedUnits.Count)];
break;
default:
break;
}
yield return new WaitForSeconds(timeBetweenEnemies);
}
yield return null;
}
Can someone point me to a better way to handle randomizing enemies or if possible, give me some sort of idea in how to do this.
Thank you.
This code doesn't take into account your separate list of ranged and melee units, but you could use this to assign a weight to every enemy's Scriptable Object, and then the enemies that spawn will be based on the weights of all of your enemies. I recommend just having one list of enemies, rather than having separate lists for melee and ranged enemies.
This code assumes your Scriptable Object is called Enemy and you add a weight field to it. If you want your weights to be specific to each wave (instead of each enemy type), then move the weight field onto the enemy's prefab instead of its ScriptableObject.
IEnumerator SpawnEnemies()
{
int totalWeight = 0;
foreach (var enemy in waves[currentWave].enemyPrefab)
{
totalWeight += enemy.GetComponent<Enemy>().weight;
}
// Spawn enemies while amount of enemies are under predetermined enemy amount.
while (spawnedEnemies < totalEnemiesInCurrentWave)
{
spawnedEnemies++;
enemiesInWaveLeft++; // I think you should be decrementing instead of incrementing?
// Determine which enemy to spawn, based on all enemy weights
var enemy = GetEnemyToSpawn(totalWeight);
var spawnedEnemy = Instantiate(enemy, spawnPoint.position, Quaternion.identity);
// Here you'll have to handle adding the enemy to the appropriate
// "enemies" list
yield return new WaitForSeconds(timeBetweenEnemies);
}
yield return null;
}
// Suppose:
// enemy A: weight 2
// enemy B: weight 6
// enemy C: weight 3
// Then
// enemy A: 2/11 chance of spawning
// enemy B: 6/11 chance of spawning
// enemy C: 3/11 chance of spawning
private GameObject GetEnemyToSpawn(int totalWeight)
{
int weight = Random.Range(1, totalWeight);
foreach (var enemy in waves[currentWave].enemyPrefab)
{
if (weight <= enemy.GetComponent<Enemy>().weight)
{
return enemy;
}
weight -= enemy.GetComponent<Enemy>().weight;
}
}
Instead of using Random.Range((int)0, (int)2) which only return 0 or 1. Use Random.Range(0f, 1f) instead which yields a float between 0 and 1:
IEnumerator SpawnEnemies()
{
const float MeleeSpawnRatio = 0.8f; // assuming a 8:2 ratio between melee/ranged
//Spawn enemies while amount of enemies are under predetermined enemy amount.
while (spawnedEnemies < totalEnemiesInCurrentWave)
{
//Randomize if Melee or Ranged enemy.
whichEnemyType = Random.Range(0f, 1f) >= MeleeSpawnRatio ? 0 : 1;

Game logic of spawer

I am student and I learn with Unity3D engine. I working on small 2D game for beginning.
I need help with some game logic. Specifically how to generate game object reasonably in 4 columns. In this game I spawn game object that falling down from top of the screen. I calculate 4 columns that cover screen. I spawn randomly game object in this 4 columns. But game play is not good. I need a advice how to do better.
My idea is spawn one or more game object in row and reasonably rotate columns. I already spawn object in row. But how to create logic for switch columns? Something like switch (now first columns, now third, now first and fourth etc.)
There is my spawn methods:
// Use this for initialization
void Start ()
{
// width of one colums
widthColum = ScreenCalc.DivideScreenInColums(colums);
...
}
public void StartSpawn()
{
spawn = StartCoroutine(Spawn());
}
IEnumerator Spawn()
{
yield return new WaitForSeconds(1.5f);
time = GameController.instance.time;
while (true)
{
// Random select from prefabs
GameObject geometryObject = spawnObjects[Random.Range(0, spawnObjects.Length)];
// X is middle of random picked columns
float x = ScreenCalc.GenerateColumPos(geometryObject, colums);
Vector3 spawnPosition = new Vector3(x, transform.position.y, 0.0f);
Quaternion spawnRotation = Quaternion.identity;
// Random object in row, will be spawned in one column
int rnd = Random.Range(1, 6);
// Spawn random count game object in one row
for (int i = 0; i < rnd; i++)
{
GameObject temp = Instantiate(geometryObject, spawnPosition, spawnRotation);
// Get Sprite render
SpriteRenderer sr = temp.GetComponent<SpriteRenderer>();
// Change color of game object
sr.color = colors[Random.Range(0, colors.Length)];
// Get script for falling down
FallingDown fl = temp.GetComponent<FallingDown>();
// Increase fall
fl.fallSpeed += 0.1f;
// Wait between spawns
yield return new WaitForSeconds(Random.Range(0.35f, 1.0f));
}
yield return new WaitForSeconds(1.0f);
}
I tried do more Spawn method, one for each column and random generate wait time. But my logic was bad, because too much object was generate or too low and collide one column each other.
Here is a picture of my idea.
On the picture you can see screen divided to 4 columns, every column is spawn position. I need to help with game logic how to switch spawn of object randomly.
For example spawn 3 object in row in column one. Then spawn 1 object in column two. Then spawn 5 object in column two and at the same time 2 object in column four.
I do not know how to explain it better. I am sorry for my English.
I think you want to spawn in each column independent of other ones.
You can add 4 spawner script for 4 columns.
Set unique column position(x) on those 4 scripts.
Or you can Start 4 coroutines for 4 columns like:
public void StartSpawn()
{
StartCoroutine(Spawn(columnPos1));
StartCoroutine(Spawn(columnPos2));
StartCoroutine(Spawn(columnPos3));
StartCoroutine(Spawn(columnPos4));
}
IEnumerator Spawn(float columnPos)
{
yield return new WaitForSeconds(1.5f);
time = GameController.instance.time;
while (true)
{
// Random select from prefabs
GameObject geometryObject = spawnObjects[Random.Range(0, spawnObjects.Length)];
// X is middle of random picked columns
float x = columnPos;
Vector3 spawnPosition = new Vector3(x, transform.position.y, 0.0f);
Quaternion spawnRotation = Quaternion.identity;
// Random object in row, will be spawned in one column
int rnd = Random.Range(1, 6);
// Spawn random count game object in one row
for (int i = 0; i < rnd; i++)
{
GameObject temp = Instantiate(geometryObject, spawnPosition, spawnRotation);
// Get Sprite render
SpriteRenderer sr = temp.GetComponent<SpriteRenderer>();
// Change color of game object
sr.color = colors[Random.Range(0, colors.Length)];
// Get script for falling down
FallingDown fl = temp.GetComponent<FallingDown>();
// Increase fall
fl.fallSpeed += 0.1f;
// Wait between spawns
yield return new WaitForSeconds(Random.Range(0.35f, 1.0f));
}
yield return new WaitForSeconds(1.0f);
}
You probably asking for an algorithm that spawn gameobject in a way that your game look interesting, Maybe this pattern will help you (you can add more steps).
Generate from right to left
Generate from left to right
Repeat above two line for random time period
Now randomly generate in four position at random point
Now Go to Step 1 repeat

Spawning objects at random generated worldpoints

I'm new to unity 3d and I want to make a very simple obstacle course game. I don't want it to have multiple levels but instead there will be only one scene which will generate randomly each time someone starts the game.
This is a picture to better explain the idea:
In each highlighted section there will be a wall which will be generated every time the application starts and the player can only get through a gap which will be randomly generated in any of the areas a, b or c of each section.
I tried looking this up but there wasn't really much of this example.
If have any questions, please don't hesitate. I'm always notified for responses.
Thanks for your time!
Basic concept:
Create a prefab from your obstacle
Create a script (e.g. WallSpawner) with a couple of parameters (distance between each wall, possible positions, etc.) and attach it to an object in your scene (in your case Walls for example).
In the Start or Awake method, create copies of your prefab with Instantiate and pass in the randomly picked position.
Example Script:
public class WallSpawner : MonoBehaviour
{
// Prefab
public GameObject ObstaclePrefab;
// Origin point (first row, first obstacle)
public Vector3 Origin;
// "distance" between two rows
public Vector3 VectorPerRow;
// "distance" between two obstacles (wall segments)
public Vector3 VectorPerObstacle;
// How many rows to spawn
public int RowsToSpawn;
// How many obstacles per row (including the one we skip for the gap)
public int ObstaclesPerRow;
void Start ()
{
Random r = new Random();
// loop through all rows
for (int row = 0; row < RowsToSpawn; row++)
{
// randomly select a location for the gap
int gap = r.Next(ObstaclesPerRow);
for (int column = 0; column < ObstaclesPerRow; column++)
{
if (column == gap) continue;
// calculate position
Vector3 spawnPosition = Origin + (VectorPerRow * row) + (VectorPerObstacle * column);
// create new obstacle
GameObject newObstacle = Instantiate(ObstaclePrefab, spawnPosition, Quaternion.identity);
// attach it to the current game object
newObstacle.transform.parent = transform;
}
}
}
}
Example parameters:
Example result:

Photon detecting if a player is already in that spawn position

I have 3 players in a room, and 3 set spawn positions, but would like each player to be spawned in one of these positions and not on the same position. I've got this, but how can I make sure that another player won't also spawn in this spawnpoint?
number = UnityEngine.Random.Range(1, 3);
if (number == 1)
{
spawnpoint = GameObject.FindWithTag("spawnpoint1");
}
if (number == 2)
{
spawnpoint = GameObject.FindWithTag("spawnpoint2");
}
if (number == 3)
{
spawnpoint = GameObject.FindWithTag("spawnpoint3");
}
GameObject myPlayer = PhotonNetwork.Instantiate(playerprefabname, spawnpoint.transform.position, spawnpoint.transform.rotation, 0);
Put the list of available spawn positions in a stack; when you need a new position pop the item off the stack and use that spawn positions.
You need to ensure that you have more spawn positions than players -- obviously.
And you also probably want to somehow randomize the stack each time.
You can take the concept and use any data structure you'd like but the important idea is to remove the "consumed" element until you are ready to reset.
You could create an array of spawn points, and randomize them and spawn your object there. With this method you can have as many spawn points as you want without having to change the code.
using UnityEngine;
using System.Linq;
public class Test : MonoBehaviour {
[SerializeField] Vector3[] spawnPoints;
[SerializeField] GameObject spawnObject;
void Start(){
InitRandom ();
}
void InitRandom(){
System.Random rnd = new System.Random ();
Vector3[] items = (
from i in spawnPoints
orderby rnd.Next()
select i
).ToArray();
foreach (Vector3 v in items) {
Instantiate(spawnObject, v, Quaternion.identity);
}
}
}
You could try getting the number of players connected to a room and use that number to select the particular spawn point.
Something like this -
var playerID = PhotonNetwork.otherPlayers.Length;
var player = PhotonNetwork.Instantiate(name, spawns[playerID].transform.position, spawns[playerID].transform.rotation, 0);
player.name = "Player " + (playerID + 1);

Categories