Instantiate GameObjects and change Material when it hits on the ground - unity3d

There is a code for instantiate cube into the list and change a Material of each clone when it hits on the ground
The following code works but not in Real-Time. Update function works like a Start function for a Foreach method
How to get a value of item.transform.position.y in the Update function Real-Time?
public GameObject cubePrefab;
public Material RedMat;
public float GroudLevel = 0.5f;
void Update()
{
if (Input.GetMouseButtonDown(0))
{
List<GameObject> cloneList = new List<GameObject>();
//instantiate clones into the list
for (int i = 0; i < 10; i++)
{
var clone = Instantiate (cubePrefab,new Vector3(Random.Range(-3f, 3f),
Random.Range(4f, 10.5f),Random.Range(-3f, 3f)), Quaternion.identity);
cloneList.Add(clone);
}
//if clone is grounded change a Material for each clone
foreach (var item in cloneList)
{
//Debug.Log(item.transform.position.y);
//check if clone is on the ground
if(item.transform.position.y < GroudLevel)
{
item.GetComponent<Renderer>().material = RedMat;
}
}
}
}
There is a screenshot for a GroudLevel = 7

The reason this function isn't working is because the pivot (transform.position) of your item is always in the center of the object. This isn't something you can change in Unity (nor would you necessarily want to).
On top of this, you're checking if the item is under the ground, and not on it when you use < insteaad of <=, because the position needs to be less than groundLevel to return true.
There are several solutions here.
The simplest would involve moving all of this logic to an OnCollisionEnter or OnTriggerEnter method. For more information on this, check the Unity documentation.
Another solution would be to find a way tthe size of the object, divide it by two, and check if
item.transform.position - halfSize <= groundLevel;
This seems really cumbersome and overly complex, however. You'd be better off using Unity's built-in collision system, unless you have a reason not to.

Related

Unity - How do I click on a specific object instead of just the whole screen in a different object?

I'm coding a top down game, with point and click movement. Currently you are able to click on the map, but you can also click outside the map to move there. I added colliders to the walls, but you still try and go outside. Code example:
if (Input.GetMouseButtonDown(1)) {'move'}
But what I want is something like this:
if (Input.GetMouseButtonDown(1) on MAP) //map is the object
So I want to be able to only click on the map, and if you click outside the map, it won't do anything. Thanks!
My script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
public float speed = 10f;
Vector2 lastClickedPos;
//Rect inRect = new Rect(82.80f, -83.20f, 164.90f, 163.29f);
bool moving;
private void Update()
{
if (Input.GetMouseButtonDown(1) && GameObject.CompareTag("clickedOn")){ // && inRect.Contains(Input.mousePosition)
lastClickedPos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
moving = true;
}
if (moving && (Vector2)transform.position != lastClickedPos)
{
float step = speed * Time.deltaTime;
transform.position = Vector2.MoveTowards(transform.position, lastClickedPos, step);
}
else
{
moving = false;
}
}
}
I think you can resolve your issue using tags or layers. I'll just list how to setup tags since it has a lot less setup vs. doing layers.
First off you'll need to create a tag and since Unity has good documentation on stuff like this I'll just link it here: https://docs.unity3d.com/Manual/Tags.html
Once you created your tag and tagged the ground/environment/area you want to have be clickable with the tag then you just need to find the object you want to collided with and use CompareTag so to put that in an example here is what your if statement could look like:
if (Input.GetMouseButtonDown(1) && collidedObject.CompareTag("TagNameGoesHere"))
{
//Movement goes here
}
Just to note I've named the gameObject that was found to collidedObject but you can name it whatever you want.
Hopefully this helps, let me know if I need to clarify something, it has been awhile since I've done a stackoverflow answer so I may have left something out.
Edit: Alright so adding onto this, you'll need to also look into how to do raycasting to check what object you click on so you can determine if it's a spot you can move to.
I've just tested this in a project just to make sure I understand it (I've used raycast a lot but never really done point to click movement before).
Essentially I've broken down things into 3 statements, which you can add together into one if statement but it's more so I can explain everything in detail:
if (Input.GetMouseButtonDown(1))
What you use currently, we want to make sure we only do the next few checks when we click
if (Physics.Raycast(playerCamera.ScreenPointToRay(Input.mousePosition), out hit, Mathf.Infinity))
So this here, is essentially drawing a line from a position, in this bit we are using the player camera which is just a regular camera reference and converting a point on screen into a ray which we then set the length to be Mathf.Infinity (this can be whatever float, I've just used this for an example) and then we output the hit object to hit which is a RaycastHit struct.
if (hit.collider != null && hit.collider.CompareTag("TagNameGoesHere"))
Now we finally check to see if the collider is not null (in case we hit the sky or something, which shouldn't happen for you in a top down game) and also that the object has right tag. Again you need to setup the tags which I've listed above and make sure you set the correct game objects in scene to have the correct tag. After which you should be able to move to the position (using the position you've setup and such is fine)
So it should look something like this:
if (Input.GetMouseButtonDown(1))
{
if (Physics.Raycast(playerCamera.ScreenPointToRay(Input.mousePosition), out rayCastHit, Mathf.Infinity))
{
if (hit.collider != null && hit.collider.CompareTag("TagNameGoesHere"))
{
//Movement goes here
}
}
}
Make sure to also put a reference to the camera and RayCastHit objects (these are the playerCamera and rayCastHit variables in the above). For my example script that I created I made them global variables.

Unity 2D - how to check if my gameobject/sprite is below ceratin Y lvl?

I've been trying to get a script working to check if my player is below a certain Y lvl, for a platformer. so it can be respawned to the beginning, But how do I put the y lvl inside a variable to check it? i cant figure it out lol
In the Update() run something like:
if(player.transform.position.y < 1)
{
//do something
}
where 'player' is the GameObject in question.
I am assuming you want to just want to compare (==, <=, >=, all that jazz is what I mean by comparing just in case you were not aware) the Y value to something like 10 for example. This is easy and you don't even need a variable necessarily for this.
//For the object position relative to the world
if(transform.position.y == 10) //"transform" gives you acces to the transform component
{ //of the object the script is attached to
Debug.Log("MILK GANG");
}
//For the object position relative to its Parent Object
if(transform.localPosition.y == 10)
{
Debug.Log("MILK GANG");
}
If you want to change the value of the position of your object then
transform.position = new Vector2(6, 9)//Nice
//BTW new Vector2 can be used if you dont
//want to assign a completely new variable
However, if you want to get a reference (Basically a variable that tells the code your talking about this component) to it.
private Transform Trans;
void Awake() //Awake is called/being executed before the first frame so its
{ //better than void Start in this case
Trans = GetComponent<Transform>();
Trans.position = new Vector2(69, 420); //Nice
}
This is the code way of doing it but there's another way that uses Unity
[SerializeField] private Transform Trans;
//[SerializeField] makes the variable changeable in Unity even if it is private so you
//can just drag and drop on to this and you good to go
Hope this help
if it doesn't
then welp I tried lel
You can use a script added to gameobject to check transform.position of the object.
if(transform.position.y < ylvl)
{
//do something
}
where ylvl is the integer of the height you want to check

Best way to create a 2d top down race track procedurally

I am attempting to create a 2d top-down car racing game. This game will have a random road map each time the player plays the game. I have thought about doing this in two different ways: A tilemap, or just generate the roads by placing different prefabs (straight roads, turns, etc). I have decided to go with the prefab route.
The way I believe it should work is to have prefab square "tiles" which have their own colliders set on the edges so I can tell if a player goes off the track in which case they blow up. I would have a MapGenerator Script which will generate an initial random map by keeping track of the last tile placed (including its location and road type: left turn, straight, right, etc). This script will then keep adding onto the road randomly as the player gets closer and closer to the end which makes it an infinite road.
I just want to know if this is just not efficient or if I am thinking of this completely wrong.
Here are a couple of images showing my road tiles which I made in photoshop and then one prefab for a straight road (take note of the colliders on its edges).
A similar game to one I want to make is Sling Drift which I can provide the link if you want. I don't know the policy on adding links to forum chat.
Also, here is my code for the map generator:
//Type of tyle, types are normal (straight road or horizontal road) and turns
public enum MapTileType
{
NORMAL,
N_E,
N_W,
S_E,
S_W
}
//structure for holding the last tile location and its type.
public struct TypedTileLocation
{
public TypedTileLocation(Vector2 pos, MapTileType tyleType)
{
m_tileType = tyleType;
m_position = pos;
}
public Vector2 m_position;
public MapTileType m_tileType;
}
public class MapGenerator : MonoBehaviour
{
//Map Tiles
public GameObject m_roadTile;
public GameObject m_turnNorthWestTile;
//holds all the tiles made in the game
private List<GameObject> m_allTiles;
//Map Tile Widths and Height
private float m_roadTileWidth, m_roadTileHeight;
//Used for generating next tile
TypedTileLocation m_lastTilePlaced;
private void Awake()
{
//store the initial beginning tile location (0,0)
m_lastTilePlaced = new TypedTileLocation(new Vector2(0,0), MapTileType.NORMAL);
//set height and width of tiles
m_roadTileWidth = m_roadTile.GetComponent<Renderer>().bounds.size.x;
m_roadTileHeight = m_roadTile.GetComponent<Renderer>().bounds.size.y;
m_allTiles = new List<GameObject>();
}
// Start is called before the first frame update
void Start()
{
SetupMap();
}
void SetupMap()
{
//starting at the beginning, just put a few tiles in straight before any turns occur
for (int i = 0; i < 6; ++i)
{
GameObject newTempTile = Instantiate(m_roadTile, new Vector2(0, m_roadTileHeight * i), Quaternion.identity);
m_lastTilePlaced.m_tileType = MapTileType.NORMAL;
m_lastTilePlaced.m_position.x = newTempTile.transform.position.x;
m_lastTilePlaced.m_position.y = newTempTile.transform.position.y;
m_allTiles.Add(newTempTile);
}
//now lets create a starter map of 100 road tiles (including turns and straigt-aways)
for (int i = 0; i < 100; ++i)
{
//first check if its time to create a turn. Maybe I'll randomly choose to either create a turn or not here
//draw either turn or straight road, if the tile was a turn decide which direction we are now going (N, W, E, S).
//this helps us determine which turns we can take next
//repeat this process.
}
}
void GenerateMoreMap()
{
//this will generate more map onto the already existing road and then will delete some of the others
}
// Update is called once per frame
void Update()
{
}
private void OnDrawGizmos()
{
}
}
Thanks!
Have you tried splines? They let you make curvy paths like race tracks easily. If not, here is a video that might help: https://www.youtube.com/watch?v=7j_BNf9s0jM.

Spawn sprites on action

I'm trying to make a Pinata GameObject, that when clicked bursts and gives a variable number of Gift GameObjects with various images and behaviors in them.
I'm also not sure what the unity vocabulary for this is so as to look this up in unity docs.
Can anyone please lend me a hand here? Thanks!
There are several ways to handle this.
The simple way is to use Object.Instantiate, Object Instantiation is the vocab you're after.
This will create a copy of a predefined Unity object, this can be a gameobject or any other object derived from UnityEngine.Object, check the docs for more info https://docs.unity3d.com/ScriptReference/Object.Instantiate.html.
In your case, your Pinata would have an array, or list, of prefabs. These prefabs are created by you with a certain behaviour and sprite for each one. When the Pinata bursts, you instantiate random prefabs at random positions surrounding the Pinata, up to you how to position these objects.
Something along these lines should do the trick:
class Pinata : Monobehaviour
{
public GameObject[] pickupPrefabs;
public int numberOfItemsToSpawn; //This can be random
//any other variables that influence spawning
//Other methods
public void Burst()
{
for(int i = 0; i < numberOfItemsToSpawn; i++)
{
//Length - 1 because the range is inclusive, may return
//the length of the array otherwise, and throw exceptions
int randomItem = Random.Range(0, pickupPrefabs.Length - 1);
GameObject pickup = (GameObject)Instantiate(pickupPrefabs[randomItem]);
pickup.transform.position = transform.position;
//the position can be randomised, you can also do other cool effects like apply an explosive force or something
}
}
}
Bare in mind, if you want the game to be consistent, then each behaviour prefab would have there own predefined sprite, this would not be randomised. The only thing randomised would be the spawning and positioning.
If you did want to randomise the sprites for the behaviours then you'd have to add this to the Pinata class:
public class Pinata : Monobehaviour
{
//An array of all possible sprites
public Sprite[] objectSprites;
public void Burst()
{
//the stuff I mentioned earlier
int randomSprite = Random.Range(0, objectSprites.Length - 1);
SpriteRenderer renderer = pickup.GetComponent<SpriteRenderer>();
//Set the sprite of the renderer to a random one
renderer.sprite = objectSprites[randomSprite];
float flip = Random.value;
//not essential, but can make it more random
if(flip > 0.5)
{
renderer.flipX = true;
}
}
}
You can use Unity random for all your random needs, https://docs.unity3d.com/ScriptReference/Random.html
Hopefully this'll lead you in the right direction.

how to randomly initialize particles system in different places of scene in unity3d

I recently tried asking this question but I realized it was not a sufficient question. In my game the player is a fire fighter learner and i want to broke out fire randomly in my game (like not predictable by player), but i did not know how to implement this.
So far i have done this but nothing goes good.(I have a empty object called t in unity which have 3 to 5 particles systems, and all are set to dont awake at start)
code is here :
using UnityEngine;
using System.Collections;
public class Example : MonoBehaviour {
public ParticleSystem[] particles;
public int numOn = 3;
public int j;
void Start() {
for (int i = 0; i < particles.Length - 1; i++) {
j = Random.Range(i + 1, particles.Length - 1);
ParticleSystem t = particles[j];
particles[j] = particles[i];
particles[i] = t;
}
for (j = 0; j < numOn; j++ )
{
particles[j].Play();
}
}
}
help will be appreciated :-)
You could try using prefabs. Create a game object in the editor that has any particle systems and scripts your fire objects need. Once it's good, drag the object from the hierarchy into your project. This will create a prefab (you can now remove it from the scene). Now, on your spawning script, add a field of type GameObject and drag the prefab you made before into it. Now, when you need to create one, just call Instantiate(prefabVar) to create a copy of your prefab.
Edit:
For your specific case, since you only want one fire to be instantiated in a random location, you could have your spawning script look something like this:
public Transform[] SpawnPoints;
public GameObject FirePrefab;
void Start() {
Transform selectedSpawnPoint = SpawnPoints[(int)Random.Range(0, SpawnPoints.Count - 1)];
Instantiate(FirePrefab, selectedSpawnPoint.position, selectedSpawnPoint.rotation);
}
This solution would allow for you to potentially spawn more than one fire object if you needed. An alternative would be if you will only ever have exactly one fire object in the scene at all. Instead of instantiating from a prefab, the object is already in the scene and you just move it to one of your spawn points at the start of the scene. An example script on the fire object itself:
public Transform[] SpawnPoints;
void Start() {
Transform selectedSpawnPoint = SpawnPoints[(int)Random.Range(0, SpawnPoints.Count - 1)];
transform.position = selectedSpawnPoint.position;
transform.rotation = selectedSpawnPoint.rotation;
}