I'm creating a game in XNA and I need a collision detection logic:
public Rectangle boundingBox = new Rectangle((int)playerShipPos.X, (int)playerShipPos.Y, frameWidth, frameHeight);
this.boundingBox = new Rectangle((int)meteorPosPub.X, (int)meteorPosPub.Y, (int)meteorTexture.Width, (int)meteorTexture.Height);
for (int i = meteorList.Count - 1; i >= 0; i--)
{
meteorGenerator meteor = new meteorGenerator(Vector2.Zero);
if (meteorList[i].meteorPosPub.Y > 664)
{
meteorList.RemoveAt(i);
if (meteor.boundingBox.Intersects(playerShip.boundingBox))
{
meteorList.RemoveAt(i);
}
}
}
So I want to achive this effect: if the player ship touches the meteor the meteor is hides and is removed from the list but nothing happens, actually.
for (int i = meteorList.Count - 1; i >= 0; i--)
{
meteorGenerator meteor = new meteorGenerator(Vector2.Zero);//you are creating new list meteors every frame, this is ridiculous
if (meteorList[i].meteorPosPub.Y > 664)
{
meteorList.RemoveAt(i);//if you are removing a position from a list, not destroying the meteor
if (meteor.boundingBox.Intersects(playerShip.boundingBox))
{
meteorList.RemoveAt(i);//you already did this, this conditional is unnecessary
}
}
}
I have no idea what it is you are doing, but this is what I would do.
1.Let the player and meteor inherit from a class with the properties a solid object would have.
Add those to a list with unique IDs based on the object type.
Every frame, check for IDs (this gives you extra control on what you want to collide with what).
Proceed to check for collisions, in case you want to remove an element, just remove it from the list and destroy it.
Related
So I'm making a project where I need an Object to mimic the properties of a set of other Objects with a certain time in between.
For that I used the following IEnumerator:
public IEnumerator GoBack(GameObject[] go)
{
for (int i = go.Length-1; i >= 0; i--)
{
if (go[i] != null)
{
GameObject g = go[i];
transform.position = g.transform.position;
sprRenderer.flipX = g.GetComponent<SpriteRenderer>().flipX;
sprRenderer.sprite = g.GetComponent<SpriteRenderer>().sprite;
Destroy(go[i]);
yield return new WaitForSecondsRealtime(rollBack);
}
}
GetComponent<Animator>().Play("EndTurnBack");
}
This basically alters my Object to be equal to the position, sprite and flipX of the object it is mimicking.
The problem is that only the position appears to work and I have no clue as to why
I tried printing in the console what was the sprite and flipX of my Object for each iteration and it is exactly what I want but the results don't appear on the screen.
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!
I'm trying to create an infinite ground for Android using Unity. Trying to use object pooling to achieve the ground repeating but is proving a bit tricky.
I can get my ground to Instantiate and create the clones along x axis.
What I am trying to achieve is to get the position of the last cloned object and set that as the new position of and create new object in the new position and instantiate again.
Do I need to work with the transform parent?
Am I going the right way about this?
Code below.
public class InfiniteGround : MonoBehaviour
{
public Transform ground1Obj;
private int count;
private Vector3 lastPosition;
void Start()
{
count = 0;
for (int i = 0; i < 10; i++)
{
Instantiate(ground1Obj, new Vector3(i * 100f, 0, 0), Quaternion.identity);
count++;
if (count == 10)
{
lastPosition = ground1Obj.position;
Debug.Log("Last Position: " + lastPosition);
}
}
}
}
For the Instantiating it should work, but not the way you intend to. If you want to have infinite ground you should add ground depending on the player position
If the player moves forward instantiate new ground before him and
destroy the old ground behind him.
If the player moves backward instantiate new ground behind him and
destroy the old ground before him
If you wanted to change your code. I would:
Change the functionname for example (InstantiateFloor), because you want to call it more than once at the start
Call the function depending on the player position (as described above)
Just instantiate 1 big floor piece (instead of 10 smaller ones) and take the position of that
Why not using the Gameobject returned by the Instantiation?
GameObject newObject = Instantiate(ground1Obj, new Vector3(i * 100f, 0, 0), Quaternion.identity);
count++;
if (count == 10)
{
lastPosition = newObject .position;
Debug.Log("Last Position: " + lastPosition);
}
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.
How can i apply Gizmos to parallel arrays of positions ?
The current code i have now is that, the Gizmos does its job.
But since its a loop, it flashes which means going through the loop and drawing an array using the method
Gizmos.DrawRay();
But i don't want it to go through the loop and draw a ray and then removing it and repeating the process with a new position.
I want it to draw a ray for every element in the array and have the ray fixed where it doesn't get rewritten.
So here is my current code.
Edit :
void Platform_Position_Scale_Generator(int i) {
posX[i] = Random.Range(minPosRange, maxPosRange + 1);
posY[i] = Random.Range(minPosRange, maxPosRange + 1);
posZ[i] = 0;
scaleX[i] = Random.Range(minScaleRange, maxScaleRange + 1);
scaleY[i] = 1;
scaleZ[i] = 1;
}
void Platform_Generator(int i) {
platformPrefabPosition[i].x = posX[i];
platformPrefabPosition[i].y = posY[i];
platformPrefabPosition[i].z = posZ[i];
Instantiate(platformPrefab, platformPrefabPosition[i], Quaternion.identity);
platformPrefab.transform.localScale = new Vector3(scaleX[i], 1, 1);
}
void OnDrawGizmos() {
if(numOfGeneratedPlatforms < numOfPlatformsToGenerate) {
Platform_Position_Scale_Generator(numOfGeneratedPlatforms);
Platform_Generator(numOfGeneratedPlatforms);
Gizmos.color = Color.blue;
Gizmos.DrawRay(new Vector3((posX[numOfGeneratedPlatforms]), (posY[numOfGeneratedPlatforms]), (posZ[numOfGeneratedPlatforms])), transform.TransformDirection(Vector3.up));
numOfGeneratedPlatforms++;
}
}
Unity as well as the SceneView need to redraw the whole area everytime something changes. There is no way to draw something that will stay. If you want it to stay you have to redraw it every time the SceneView is redrawn. Unlike at runtime, at edittime Unity only redraws a window when some event happened.
So if you want to have a line for each position you have to draw a line for each position in OnDrawGizmos, everytime it's called. The SceneView works the same as the GameView. However it uses an invisible camera to render the scene from the editors perspective. Each time it is redrawn the SceneView will be cleared and completely redrawn. During that process Unity will call your OnDrawGizmos method where you have to draw everything you want to be visible in the scene.
edit
To draw all lines for all elelments you just have to use a loop, like this:
void OnDrawGizmos()
{
Gizmos.color = Color.blue;
for(int i = 0; i < posX.Length; i++)
{
Gizmos.DrawRay(new Vector3(posX[i], posY[i], posZ[i]), transform.up);
}
}
This will draw a one unit long line from each point in your list upwards. As i said if you work with positions it's usually way easier to work with a single array and use Vector3 has type:
public Vector3[] positions;
Or if you have to add / remove items quite often, use a generic List:
public List<Vector3> positions
ps: To me it looks a bit strage that you want to draw a line upwards. If you wanted to draw a line from point to point, you have to do:
void OnDrawGizmos()
{
Gizmos.color = Color.blue;
// important to reduce the upper limit by one since we add 1 inside the loop
for(int i = 0; i < posX.Length-1; i++)
{
Gizmos.DrawLine(
new Vector3(posX[i], posY[i], posZ[i]),
new Vector3(posX[i+1], posY[i+1], posZ[i+1])
);
}
}