Changing GameState based on an if statement? - c#

I'll get to the point. Having an issue with a beginner level game im coding at the moment. I have 2 lists to store "Objects" that are in the game. One is for "diamonds" pushable blocks that are to be moved onto the "Goals". Once all diamonds are on the goals, the level should change. I'm currently using "GameStates" to load each level. Here is the snippet of my code I'm having issues with. What happens currently is the game will allow me to push the "diamonds" onto the "goals", but the gamestate will not change once I do this. Not sure what I am missing - any help is appreciated. Thankyou for your time!
void Level1Update(KeyboardState cKB, KeyboardState oKB)
{
for (int i = 2; i < diamondlist.Count; i++)
{
if ((Goallist[i].Position == diamondlist[i].Position))
{
CurrentGameState = GameState.Level2;
}
}
}

If I understood correctly, you want to set current game state to the next level only if all the diamonds and goals are on the same tiles. The following code ensures that.
void Level1Update(KeyboardState cKB, KeyboardState oKB)
{
int i;
for (i = 0; i < diamondlist.Count; ++i)
{
if (Goallist[i].Position != diamondlist[i].Position)
break;
}
// When breaked off the loop, i is never equal to list count
if (i == diamondList.Count)
CurrentGameState = GameState.Level2;
}

Related

Gameobject not spawning in desired position after InvokeRepeating and Instantiate

Context
Hello, currently creating a clone of "Crossy Road" and what I'm trying to do is to spawn my moving object called "Vehicle" at a random speed and rate of spawn. This is also applicable to "Plank", but I will start first with the vehicle. So far, everything is working fine as intended for the game mechanics, but I would like to finalize with this issue so it is fully functional in terms of playability.
Problem
My issue now is I 3 different spawns objects: grass, river, and road. Each object holds other objects (let's call it spawners) depending of what field is being spawn. For example, if grass field object is spawned, it will spawn trees depending in a random varied selection. Another example is with road field. When the road is spawned, a vehicle will be spawned from either left or right in its current initial position. This vehicle will moves as intended with a random speed, but not with the original spawn position and rate (as shown in the GIF. The vehicle spawns in the middle of the road and not in the beginning of the left/right road).
As far I'm aware, my rate is currently unused because it is not the main issue I want to solve. However, the issue now is with the transform position not working as I have pictured in my head. So what is happening is that when the road is spawned again, the vehicle is spawned in the middle of the trajectory instead of resetting to the beginning.
Also, I have noticed that when I print the vehicle object, the Z-axis has a weird number compared to the original position.
Attempts done
I have been thinking that maybe it is the way I have set everything up. I have 4 vehicle objects with a child object called "Tank". However, in each vehicle object, I'm using SetActive(...) only and not really reusing the object itself to the beginning. Later on, I want to organize this spaghetti code and optimize it (e.g ObjectPool to spawn my roads and other GameObjects after hitting a certain range, adding a player range detection to spawn a field to name a few).
To be honest, my whole code feels bloated for something simple. This will be fixed once everything is working accordingly.
Code (DISCLAIMER: there is the possibility that there are unused variables)
SpawnManager.cs (some links provided too from learning to make this)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/*
** Weighted randomness: https://forum.unity.com/threads/random-numbers-with-a-weighted-chance.442190/
** Scriptable Object Weight spawn example: https://www.youtube.com/watch?v=FCksj9ofUgI&ab_channel=LlamAcademy
** From scratch loot tables with Scriptable Objects to make a loot table: https://www.youtube.com/watch?v=tX3RWsVLnzM&ab_channel=GregDevStuff
** Creating a random with an animation curve: https://www.youtube.com/watch?v=zw1OERK5xvU&ab_channel=HamzaHerbou
** Random Vehicle position spawn (maybe this can help me): https://stackoverflow.com/questions/51312481/move-spawn-object-to-random-position
*/
public class SpawnManager : MonoBehaviour
{
public GameObject Player;
public Spawn[] Field;
public GameObject[] SpawnObjectTrees;
public GameObject[] SpawnObjectVehicles; //different vehicles
public GameObject[] SpawnObjectPlanks; //3 sizes (small, medium, large)
private PlayerControl2 playerControlScript;
private int distancePlayer;
private int toggle;
private bool keepSpawning;
bool vehicleFlag = false;
bool plankFlag = false;
public float randomNumSpawn;
void Awake()
{
keepSpawning = true;
playerControlScript = GameObject.Find("PlayerObject").GetComponent<PlayerControl2>();
InvokeRepeating("Spawner", 3f, randomNumSpawn);
}
void Update()
{
if (Input.GetButtonDown("up") && !playerControlScript.gameOver)
SpawnField();
}
void Spawner()
{
bool activeLeft = false;
bool activeRight = false;
if (vehicleFlag)
{
print(initialObjectSpawn);
for (int i = 0; i < SpawnObjectVehicles.Length; i++)
{
print($"{SpawnObjectVehicles[i]}: {SpawnObjectVehicles[i].transform.position}"); //Here I get the weird position.z values pretty wonky
toggle = Random.Range(0, 2);
if (toggle == 1 && !activeLeft)
{
activeLeft = true;
SpawnObjectVehicles[i].SetActive(true);
}
if (toggle == 0 && !activeRight)
{
activeRight = true;
SpawnObjectVehicles[i].SetActive(true);
}
else
SpawnObjectVehicles[i].SetActive(false);
}
}
}
void SpawnField()
{
//I want to spawn the vehicles, planks, and trees in sets accordingly to the field (grass, river, road)
//For vehicles and planks, they can move horizontally from either -z or z boundaries
//NOTE: keepSpawning may be useless if i have a playerControlScript.gameOver already in here
if (keepSpawning)
{
distancePlayer += 3;
Vector3 intPos = new Vector3(0, 0, 0);
int i = Random.Range(0, 1000);
for (int j = 0; j < Field.Length; j++)
{
if (i >= Field[j].minProbabilityRange && i <= Field[j].maxProbabilityRange)
{
intPos = new Vector3(distancePlayer, -1f, 0);
GameObject Surface = Instantiate(Field[j].spawnField);
if (Surface.CompareTag("Grass"))
TreeToggle();
if (Surface.CompareTag("Road"))
{
vehicleFlag = true;
VehicleToggle();
}
// if (Surface.CompareTag("River")) this will be the same as vehicle
// {
// plankFlag = true;
// PlankToggle();
// }
//Add spawn for vehicles and planks with given spawnrate/spawn intervals
Surface.transform.position = intPos;
vehicleFlag = false;
plankFlag = false;
}
}
}
}
void TreeToggle()
{
int counter = 0;
for (int i = 0; i < SpawnObjectTrees.Length; i++)
{
int toggle = Random.Range(0, 2); //[0, 2)
if (toggle == 1 && counter < 5) //True and when there are already 5-4 trees to toggle
{
counter++;
SpawnObjectTrees[i].SetActive(true);
}
else //fills the rest to inactive Trees
SpawnObjectTrees[i].SetActive(false);
}
}
void VehicleToggle()
{
// I have Left and Right with 2 vehicles in each. My goal is to setActive one of them each side at a time with a different interval spawnrate and speed
Spawner();
}
void PlankToggle()
{
Spawner();
}
}
[System.Serializable]
public class Spawn
{
public GameObject spawnField;
public float minProbabilityRange = 0.0f;
public float maxProbabilityRange = 0.0f;
}
Hierarchy/Inspector
If there is any information you want to know, feel free to ask and I will make a quick edit to fulfill these goals. Again, thank you for your time and appreciate it :D I hope you are having a good day!

PlayerPrefs not saving unlocked levels

I used this code to try and make buttons in my level select screen not interactable if the level has not been completed. By default, I want to set level one's button to be unlocked so I used this:
[SerializeField] Button[] levelBUttons;
private void Start()
{
int levelCurrentlyAt = PlayerPrefs.GetInt("levelCurrentlyAt", 2);
for (int i = 0; i < levelBUttons.Length; i++)
{
if(i+2 > levelCurrentlyAt)
{
levelBUttons[i].interactable = false;
}
}
}
The problem is when I press play, level one's button is also not interactable
The reason I have set the int to 2 and not 1 is that in the build settings the scene order is 1) Start Screen, 2) Level Select Screen 3) Level One.
In the inspector, level one's button is also the first button element in the button array.
I changed the for loop to this and it seems to work:
int levelAt = PlayerPrefs.GetInt("levelAt", 1);
for (int i = 0; i < levelBUttons.Length; i++)
{
if(i+1>levelAt)
{
levelBUttons[i].interactable = false;
}
}
But is this not wrong because level one has a build index of 2 and 1, I am supposed to use i+1 and not i+2.
Not sure I fully understand your question, but you might be getting confused on the order. The first scene in build order will have index of 0, as that's how indexing in most languages works. Thus level 1 will have index of 1, unless you have 2 scenes before it in the build menu. Hope I could help!

Removing and re-adding same sprite to list Unity

I have a Unity script in c# where a public list of sprites is declared and then populated manually in the inspector.
The list consists of 19 sprites of cars.
These cars are available only if the player completed each level.
For example: When you open the game for the first time, you only have 1 available car but if you complete level 1, you unlock another car, when you complete level 2, you get another and so on.
My question is, being that I manually populated the list, how do I go about re-adding the same sprite back when a level is completed?
I was going to use cars.RemoveRange(1, 18) to remove them from the list but do not know a way to add them back without calling every sprite back manually. I believe there is a simpler, better way that I do not know about.
This is the script:
public class CarSelection : MonoBehaviour
{
public List<Sprite> cars; //store all your images in here at design time
public Image displayImage; //The current image thats visible
public Button nextCar; //Button to view next image
public Button previousCar; //Button to view previous image
private int i = 0; //Will control where in the array you are
void OnEnable()
{
//Register Button Events
nextCar.onClick.AddListener(() => NextCarButton());
previousCar.onClick.AddListener(() => PreviousCarButton());
}
private void Start()
{
cars.RemoveRange(1, 18);
if (FinishLineTouch.levelOneComplete == true) {
//Re-adding the same sprites here
}
}
public void NextCarButton()
{
if (i + 1 < cars.Count)
{
i++;
}
}
public void PreviousCarButton()
{
if (i - 1 >= 0)
{
i--;
}
}
void Update()
{
if (LevelSelect.isCarChosen == true) {
nextCar.interactable = false;
previousCar.interactable = false;
}
displayImage.sprite = cars[i];
}
}
Thanks in advance.
You have a small flaw in your logic here.
Saying that you add the car sprites in the inspector, removing all the sprites is not ideal.
You can have a list of all the sprites, and a list with available options (starts with sprite[0] and adds each time you complete the level).
However, if you want to know how to add them back after deleting them, you need to make a copy in a global variable, so it's more efficient to try what I am suggesting above.
You should have two different arrays, one for the sprites, and the other one for if the player unlocked it yet. Or you should create a custom class that has the sprite of the car, and if it is unlocked yet.
Sounds like all you really need to do is make sure the index doesn't exceed the unlocked cars
if(i + 1 < cars.Count && i + 1 <= amountOfFinishedLevels)
{
i++;
// and then you really should do this in both methods and not every frame
displayImage.sprite = cars[i];
}
and don't remove/readd any items at all.

UNITY c#: Spawning character prefabs based on user selection

I have game for multiple players where each user selects their hero before game starts and that loads the selected heroes into the battle arena.
I have small issue with getting the instantiation to spawn in correct numbers of players
The method that I have for Spawning the characters:
private void Placement()
{
for (int i = 0; i < SelectedCards.Count; i++)
{
for (int t = 0; t < AvailableHeroes.Count; t++)
{
if (AvailableHeroes[t].name == SelectedCards[i].name)
{
Debug.Log(AvailableHeroes[t]);
// Instantiate(AvailableHeroes[t], PlayerSpawnLocation[t].transform.position, transform.rotation);
}
{
}
}
}
}
This script checks for amount of selected hero cards and puts it against my list that has all the available heroes to choose from(prefabs).
The debug.log shows that only the correct heroes get called.
Instantiate ends up spawning a loot of heroes instead of the selected amount.
For clarity I attach full class:
{
private int playerSize; //amount of choices for card selection
private GameManager GM;
[Header("Lists for Spawning in Heroes")]
public List<GameObject> SelectedCards;
public List<GameObject> AvailableHeroes;
public List<Transform> PlayerSpawnLocation;
[Header("Canvas used for the game")]
public Transform GameCanvas;
public Transform CharacterCanvas;
//When scene starts it takes how many players will be picking a card.
void Start()
{
//connects this script with gamenmanager to be able to manipulate the cameras
GM = GameObject.Find("GameManager").GetComponent<GameManager>();
//gets playersize information from main menu selection
PlayerPrefs.GetInt("PlayerSize");
playerSize = PlayerPrefs.GetInt("PlayerSize");
SelectedCards = new List<GameObject>();
//enables/disables correct canvas not to cause any problems when we initiate this scene
GameCanvas.gameObject.SetActive(false);
CharacterCanvas.gameObject.SetActive(true);
}
// Update is called once per frame
void Update()
{
if (playerSize <= 0)
{
Placement();
GM.CharacterSelectionCamera.enabled = false;
GameCanvas.gameObject.SetActive(true);
CharacterCanvas.gameObject.SetActive(false);
GM.BattleCamera.enabled = true;
}
}
public void PlayerSelected(int cardPicked)
{
playerSize -= cardPicked;
}
private void Placement()
{
for (int i = 0; i < SelectedCards.Count; i++)
{
for (int t = 0; t < AvailableHeroes.Count; t++)
{
if (AvailableHeroes[t].name == SelectedCards[i].name)
{
Debug.Log(AvailableHeroes[t]);
// Instantiate(AvailableHeroes[t], PlayerSpawnLocation[t].transform.position, transform.rotation);
}
{
}
}
}
}
}
I hope someone can explain where I am going wrong with this.
Thanks,
I got the answer, I guess I was just being tired from working and could not see the obvious.
For those who wonder what solution is
The method gets called each frame thus it continues to endlessly spawn objects
There are 2 ways to fix it
1 Make coroutine and then return after you make your initial batch
2 Use a boolean at update so not only it checks player size but also whenever it can spawn it or not, you set the boolean to false after method get called.
I did not even notice the update function part.
Just a heads up, in your start function, PlayerPrefs.GetInt("PlayerSize"); is not doing anything since the value is not saved anywhere.

Unity - Logic to pick up objects and drop them one by one at different positions

Please look at example - http://www.mathplayground.com/mancala.html
Can anyone suggest the logic to :
1) spawn objects at positions
2) Pick up all objects on click and distribute them one by one.
3) Is it better to create all objects or instantiate them on the fly. ?
I tried code below but it just instantiates all objects at once.
if (HoleHandler.gemCount_Hole1_Update_Flag == true)
{
foreach (GameObject g in gemList1)
{
Destroy(g);
//want to add a time delay of 2 secs here
}
if (gemCount_Hole1 > 0)
{
for (int i = 0; i < gemCount_Hole1; i++)
{
int Gem_prefabIndex = UnityEngine.Random.Range(0, 9);
gemList1.Add(Instantiate(Gem_prefabList[Gem_prefabIndex], new Vector2((xPos_Hole1 + (Random.Range(-20, 20))) * 2.0F, (-229 + (20 * i))), Quaternion.identity));
}
}
}
I'm not 100% sure of what you're trying to achieve but I will answer as best I can.
For a start, any gameobject you are going to be instantiating (spawning) at run time should ideally be done so from a prefab.
Secondly, to spawn them at random intervals you want to be checking if they should be spawned at different time frames. This can be achieved through a co-routine or the Update function. I would recommend Update if this is new to you.
Update is called every frame.. and it's with this that you can achieve timed events. You can use a variety of helper methods to determine the time since the last frame or the real time elapsed.
For example
public class MyGameObject : Monobehaviour {
void Start() {
//This is called first, use it to set up whatever you want.
}
void Update() {
//This will be called every frame.
//Each frame or time lapse will determine if I should spawn
// a new gameobject.
}
}
Update
After looking at the game you have linked in your post I can offer the following advice.
Something like the following may point you in the right direction.
public int[] gemsInCups = new int [] {4,4,4,4,4,4,0,4,4,4,4,4,4,0};
public void Distribute(int position){
int gems = gemsInCups[position];
for(int i = position + 1; gems > 0; i++){
gemsInCups[position] ++;
gems --;
//Check the end of the array has not been reached.
//If it has, start distributing again from the first position provided
// there are still gems to distribute.
}
}
You will need some additional logic to finish this.
What you should remember is, I usually find it much more manageable keeping my data and my view (gameobjects) under different scopes... but the view will change to reflect the data and does not directly alter it. Now you know how many gems there are in each cup, you can simply update this each frame.

Categories

Resources