Is it possible to include a variable inside of a vector3? - c#

I trying to make a game kind of like slope, where there are new obstacles every 3 seconds.
Instead of making infinite spawn points, I thought of making the first one and change the z by 20 pixels. the problem is I don't know how I can make a Vector3 storing 2 integers and a variable.
I'm kind of stuck so I haven't tried anything since I don't know what to try.
using System.Collections.Generic;
using UnityEngine;
public class levelgen : MonoBehaviour
{
private int count = 9;
public GameObject[] templates;
// Update is called once per frame
void Update()
{
public Vector3 spawn = new Vector3(-2, 0, count);
int rand = Random.RandomRange(1, 5); //1-5 types of levels
Instantiate(templates[rand], spawn1, Quaternion.identity);
count = count + 20;
}
}
I want to store the variable count in the Vector3 spawn.

You can't store anything else into the Vector3 or any other built in variables*. You can, and you should make custom containers for your variables like classes or structs
public struct Custom
{
int a;
int b;
string name;
int count;
}
or something like
public struct Custom
{
Vector3 vec;
int count;
}

Sure you can .. but it will not be called count anymore but e.g. z
public class levelgen : MonoBehaviour
{
// You can not declare a public field within a method
// so move it to class scope
public Vector3 spawn = new Vector3(-2, 0, 9);
public GameObject[] templates;
// Update is called once per frame
void Update()
{
// Here NOTE that for int parameters the second argument is "exclusiv"
// so if you want correct indices you should always rather use
int rand = Random.RandomRange(0, templates.Length);
// I will just assume a typo and use spawn instead of spawn1
Instantiate(templates[rand], spawn, Quaternion.identity);
spawn.z += 20;
// Or I would prefer since this works in general
// Vector3.forward is a shorthand for writing new Vector3(0, 0, 1)
// and this works in general
spawn += Vector3.forward * 20;
// you can e.g. NOT use
//transform.position.z += 20
// but only
//transform.position += Vector3.forward * 20;
}
}
Note Having this code Instantiate a new object every frame in general is a very bad idea. If you really need so many objects checkout Object Pooling

Every time you want to change the z-axis for a new spawn you will need to reassign it to your Vector3 variable. You could do this in a for loop as seen below.
zPosition in this case is storing the latest zPosition value so you don't need to store it anywhere else. If you want to spawn more obstacles after the initial 10 then it will pick up from the zPosition where it left off.
public class levelgen : MonoBehaviour
{
public GameObject[] templates;
public Vector3 spawn;
int zPosition = 0;
void Start()
{
GenerateObstacles(10);
}
void GenerateObstacles (int numObstacles)
{
for (int i = 0; i < numObstacles; i++)
{
spawn = new Vector3(-2, 0, zPosition);
int rand = Random.Range(0, 6); //1-5 types of levels
Instantiate(templates[rand], spawn, Quaternion.identity);
zPosition += 20;
}
}
}

Related

Random Script repeatedly going down

I am busy building a platformer(2d). And I really want procedural generation. I think that I came up with a smart system for spawning the platforms randomly(up for debate). But my platforms keep on picking to randomly spawn down. No matter how many times I restart the game
eg :
here is my code
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlatformSpawner : MonoBehaviour
{
public GameObject[] platform;
public Vector2 SpawnGap;
public float randomspawn;
public float VerticalRandomness;
public float queueTime = 1.5f;
private float time = 0;
public Vector2 startspawn;
public GameObject flag;
Vector2 nextspawn;
Vector2 nextspawncentre;
Vector3 prevspawn;
public int platamount;
int amountspawned;
bool alreadyspawnedflag;
// Start is called before the first frame update
void Start()
{
platamount = Random.Range(5, 15); // random amount of platforms in level
alreadyspawnedflag = false;
}
// Update is called once per frame
void Update()
{
if (time > queueTime) // just for quality of life(so that if I maybe have 10000 platforms I can delay the spawning)
{
if(platamount >= amountspawned) // random amount of platform
{
GameObject go = Instantiate( platform[ Random.Range (0, platform.Length) ] ); // instantiating random object from list, to make it even more random(objects assigned in inspector)
Vector2 randomrange = new Vector2(Random.Range(1f, -1f) * randomspawn, Random.Range(2f, -0.25f/*perferes up, but stil not working*/) * VerticalRandomness);//calculates random position
Debug.Log(randomrange);// it is indeed choosing random pos, even on y but still giong down
nextspawncentre = new Vector2(prevspawn.x + SpawnGap.x + startspawn.x, prevspawn.y + SpawnGap.y + startspawn.y); // calculates next spawn without randomness
nextspawn = new Vector2(nextspawncentre.x + randomrange.x, nextspawncentre.y + randomrange.y);// adds randomness
go.transform.position = transform.position + new Vector3(nextspawn.x, nextspawn.y, 0);// places object at nextspawn variable
time = 0;// resets timer
prevspawn = go.transform.position;
amountspawned += 1;
randomrange = new Vector2(0, 0);// tried reseting the random, but to no success
}
else if(!alreadyspawnedflag)// almost same logic as before just for the flag to reload scene(handeled in another script)
{
GameObject spawnedflag = Instantiate(flag);
spawnedflag.transform.position = new Vector3(nextspawncentre.x + 1, 15, 0f);
alreadyspawnedflag = true;
}
}
time += Time.deltaTime;
}
}
look at comments explained, everything there.
In advance thanks for the help.
ps: Platforms do not have rigidbodies, just sprite renderer and boxcollider2d.
I think the issue is related to your startSpawn being a negative value (at least it looks like that from your first platform). So even if the Y is positive in the randomrange it might be overruled by the negative startSpawn Y value.
Actually I think you don't even add your startSpawn every time at all but rather only using it as the initial position
I think you should simply do e.g.
private bool firstPlatform = true;
and
...
if(firstPlatform)
{
nextspawn = startspawn;
firstPlatform = false;
}
else
{
nextspawncentre = prevspawn + SpawnGap;
nextspawn = nextspawncentre + randomrange;
}
...

What is the fastest method to create and render large number of 2d sprites in Unity?

I am creating an infinite terrain generator and I need to constantly update the terrain as the player moves around.
Everything is fine, but I am having trouble with finding information about the fastest method of creating and rendering sprites on the fly.
Information about sprites:
I am using 1 sprite sheet which has all the frames I need for my terrain. Grass, sand, water etc. all in 1 single .png file. All frames are stored in an array from which I can easily grab them.
Steps I need to do to display my sprite object correctly currently:
Create new object.
Set their position in 2d space.
Add component.
Scale them as needed.
Generated sprites get stored in a GameObject array called chunk. This is the way I am currently generating sprites.
chunk[i] = new GameObject();
chunk[i].gameObject.transform.position = new Vector2(spriteCoordX, spriteCoordY);
chunk[i].AddComponent<SpriteRenderer>();
SpriteRenderer renderer = chunk[i].GetComponent<SpriteRenderer>();
renderer.sprite = tiles[calculatedFrameId]; //Set correct sprite frame.
chunk[i].gameObject.transform.localScale = new Vector2(6.75f , 6.75f);
I don't know, adding component and scaling every single time I want to create a new sprite in code seems redundant and unnecessary and I am sure there is a better way to do that.
To sum up:
I need the best (fastest) possible way to generate large number of sprites, set their frame, position and proper scale.
Cannot help posting this image here, as this is really of thousands of words, thanks #AidenH for the "Object pooling" comment!
I apologize this took me some time to get to... I ended up creating a project where I generate an open world, and I use Perlin Noise to generate portions of the map depending on where the player is(Since I dont have an actual terrain)
I used Object Pooling for this, so I have a World Manager that knows what the world looks like (or in my case it uses Perlin Noise), this WorldManager pools tiles.
NOTE: This is one way to do it, using Object Pooling.
So basically it would look something like this:
public class WorldManager : MonoBehaviour {
// Being cheap here for the time, if you use this route make sure to use a proper singleton pattern
static public WorldManager instance;
[SerializeField] // Created a prefab for all my tile objects
private TileObject tilePrefab;
[SerializeField]
private int StartingTilePool = 300;
[SerializeField] // In my case this list stored 1 sprite, and I just changed that sprite color depending on the value of perlin noise
List<Sprite> terrainSprites;
private Queue<TileObject> objectPool = new Queue<TileObject>();
void Start() {
instance = this; // Again use a proper singleton pattern in your project.
GenerateTilePool();
LoadFirstSetOfTiles();
}
private void LoadFirstSetOfTiles()
{
// my player always starts at 0,0..
for(int x = -SpawnTileBoundry.HorizontalExtents; x <= SpawnTileBoundry.HorizontalExtents; ++x)
{
for(int y = -SpawnTileBoundry.VerticalExtents; y <= SpawnTileBoundry.VerticalExtents; ++y)
{
SetActiveTile(x,y);
}
}
}
private void GenerateTilePool()
{
for(int i =0; i < tilesToGenerate; ++i)
{
TileObject tempTile = Instantiate(tilePrefab);
EnqueTile(tempTile);
}
}
public void EnqueTile(TileObject tile)
{
objectPool.Enqueue(tile);
tile.gameObject.SetActive(false);
}
public void SetActiveTile(int x, int y)
{
TileObject newTile = null;
if(objectPool.count > 0)
{
newTile = objectPool.Dequeue();
}
else
{
// We didn't have enough tiles store in our pool so lets make a new 1.
newTile = Instantiate(tilePrefab);
}
newTile.transform.position = new Vector3(x,y,1); // Used 1 to put it behind my player...
newTile.gameObject.SetActive(true);
// The sprite here would be based off of your world data, where mine is only a white box, I use the second parameters to give it a gray-scaled color.
newTile.UpdateSprite(terrainSprites[0], Mathf.PerlinNoise(x/10.0f, y / 10.0f));
}
}
That is just my WorldManager that handles ObjectPooling for me...
Here is my TileObject
[RequireComponent(typeof(SpriteRenderer))]
public class TileObject : MonoBehaviour {
private SpriteRenderer myRenderer;
private void Awake() {
myRenderer = getComponent<SpriteRenderer>();
}
void Update()
{
if(Mathf.Abs(transform.position.x - Camera.main.transform.position.x) > SpawnTileBoundry.HorizontalExtents || Mathf.Abs(transform.position.y - Camera.main.transform.position.y) > SpawnTileBoundry.VerticalExtents)
{
// With this check the tile knows it is no longer visible,
// I could have used OnBecameInvisible to handle this
// but I added a threshold to the extents, that prevent it so
// players would see tiles "appearing" this caused a nice bug, where
// if you moved just right a row/col of tiles wouldn't spawn.
WorldManager.instance.EnqueTile(this);
}
}
public void UpdateSprite(Sprite sprite)
{
myRenderer.sprite = sprite;
}
public void UpdateSprite(Sprite sprite, float grayColor)
{
UpdateSprite(sprite);
myRenderer.color = new Color(grayColor,grayColor,grayColor,1f);
}
}
Here is my SpawnTileBoundry script:
public class WorldManager : MonoBehaviour {
private int lastX = 0;
private int lastY = 0;
static public int HorizontalExtents;
static public int VerticalExtents;
void Start() {
VerticalExtents = (int)Camera.main.orthograpicSize + 2; // +2 is just my threshold
HorizontalExtents = (int)(VerticalExtents * Screen.width/Screen.height) +3; // +3 is just my threshold you can change these to what you want.
lastX = (int)transform.position.x;
lastY = (int)transform.position.y;
}
void Update() {
int newX = (int)transform.position.x;
int newY = (int)transform.position.y;
HandleNewTileSpawn(lastX - newX, lastY - newY);
}
// This will tell the WorldManager which tiles need to appear
// We are no longer creating new tiles unless we absolutely have to.
// We are also only making new tiles appear in the circumstance that
// we are about to see them.
void HandleNewTileSpawn(int x, int y)
{
if(x != 0)
{
// This code could be refactor to a method so it was less error prone for changes or tweaks...
if(x < 0)
{
for(int i = lastY - VerticalExtents; i < lastY + VerticalExtents; ++i)
{
WorldManager.instance.SetActiveTile(lastX + HorizontalExtents, i);
}
}
else
{
for(int i = lastY - VerticalExtents; i < lastY + VerticalExtents; ++i)
{
WorldManager.instance.SetActiveTile(lastX - HorizontalExtents, i);
}
}
lastX = (int)transform.position.x;
}
if(y != 0)
{
if(lastY < 0)
{
for(int i = lastX - HorizontalExtents; i < lastX + HorizontalExtents; ++i)
{
WorldManager.instance.SetActiveTile(i, lastY + VeritcalExtents);
}
}
else
{
for(int i = lastX - HorizontalExtents; i < lastX + HorizontalExtents; ++i)
{
WorldManager.instance.SetActiveTile(i, lastY - VeritcalExtents);
}
}
lastY = (int)transform.position.y;
}
}
}
I am using the WorldManager to handle my object pooling, yes I am still instantiating quite a few sprites at the beginning but then I eventually stop spawning as there is no need to keep spawning new objects.
Unity doesn't have alot in their documentation in regards to object pooling however they do have a video tutorial that goes over some of the basics of Object Pooling: https://unity3d.com/learn/tutorials/topics/scripting/object-pooling

Unity random object generation always returning same object

In the vertical game, I am creating random blocks spawn within a 100f. I am randomly creating 3 different types of objects which are 'platforms' 'boostplatforms' and 'breakableplatforms'. My code for generating all of these give no errors and when I run the game the blocks generated when I check the activity however visually only the objects named 'platform' appear.
The coding I used for this was:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class LevelGenerator : MonoBehaviour {
public GameObject platformPrefab;
public GameObject platformPrefab2;
public GameObject platformPrefab3;
public int numberOfPlatforms = 999;
public int numberOfBoostPlatforms = 999;
public int numberOfBreakablePlatforms = 999;
public float levelWidth = 100f;
public float minY = 55f;
public float maxY = 120f;
// Use this for initialization
void Start () {
Vector3 startPosition = new Vector3 ();
for (int i = 0; i < numberOfPlatforms; i++)
{
startPosition.y += Random.Range (minY, maxY);
startPosition.x = Random.Range (-levelWidth, levelWidth);
Instantiate (platformPrefab, startPosition, Quaternion.identity);
}
for (int i = 0; i < numberOfBoostPlatforms; i++)
{
startPosition.y += Random.Range (minY, maxY);
startPosition.x = Random.Range (-levelWidth, levelWidth);
Instantiate (platformPrefab2, startPosition, Quaternion.identity);
}
for (int i = 0; i < numberOfBreakablePlatforms; i++)
{
startPosition.y += Random.Range (minY, maxY);
startPosition.x = Random.Range (-levelWidth, levelWidth);
Instantiate (platformPrefab3, startPosition, Quaternion.identity);
}
}
// Update is called once per frame
void Update () {
}
}
Based on the information provided, I would recommend the following:
Verify that your prefab variables are set to the correct prefabs.
Notice that each platform's Y position is cumulative. Verify that the boost and break platforms aren't simply spawning after all of the standard platforms (which they will be based on your code).
Switch to object pooling. You are spawning 3,000 objects in a single frame even though the majority won't even be visible. The idea would be to spawn a small pool, 10 of each let's say, and when a new platform is needed (coming into view) you recycle one of the existing objects that is no longer visible or needed. Unity's Object Pooling tutorial

How do I fix this "Does Not Contain Definition" Error?

So I'm working on a project for class in which, using Unity3D, I'm instatiating fish game objects into a game and making them appear randomly across the background. Currently, I have this code that's supposed to make my critter appear. I wrote two scripts that are supposed to make my critter game objects appear on different sides of the screen
using UnityEngine;
using System.Collections;
public class Wrangler : MonoBehaviour {
public static float halfWidth = 6;
public static float halfHeight = 5;
public GameObject critterPrefab;
public GameObject crabPrefab;
int critterCount = 0; //how many we've made so far
int critterMax = 5; //how many you want
int crabCount = 0;
int crabMax = 5;
// how long between critter spawns
void Start()
{
StartCoroutine("SpawnCritter");
StartCoroutine("SpawnCrab");
}
IEnumerator SpawnCritter()
{
while (critterCount < critterMax)
{
CreateCritter();
yield return new WaitForSeconds(1.0f);
}
}
IEnumerator SpawnCrab()
{
while (crabCount < crabMax)
{
CreateCrab();
yield return new WaitForSeconds(1.0f);
}
}
void CreateCritter() {
critterCount++;
Instantiate(critterPrefab);
}
void CreateCrab() {
crabCount++;
Instantiate(crabPrefab);
}
}
using UnityEngine; //allows us to use objects and stuff from Unity Engine
using System.Collections;
//creates a class we can manipulate called "Critter" and allows it to do the
//same thing MonoBehavior does
public class Critter : MonoBehaviour {
//variables here are for the class to use and functions can use them any time
//It looks like you can create variables first, then create the functions
protected Rigidbody rb;
// Use this for initialization
public virtual void Start () {
float startX = Random.Range (-Wrangler.halfWidth, Wrangler.halfwidth);
float startY = Random.Range (-Wrangler.halfHeight, Wrangler.halfheight);
transform.position = new Vector2 (startX, startY);
rb = GetComponent<Rigidbody> ();// Functions make classes do stuff
setVelocity ();
}
public virtual void setVelocity() {
rb.velocity = new Vector2 (-1,0);
}
// Update is called once per frame
void Update () {
if (transform.position.x > Wrangler.halfWidth)
transform.position = new Vector2 (-Wrangler.halfWidth, transform.position.y);
if(transform.position.x < Wrangler.halfWidth)
transform.position = new Vector2 (Wrangler.halfWidth, transform.position.y);
if(transform.position.y > Wrangler.halfHeight)
transform.position = new Vector2 (-Wrangler.halfHeight, transform.position.y);
if(transform.position.y < Wrangler.halfHeight)
transform.position = new Vector22 (Wrangler.halfHeight, transform.position.y);
}
}
I keep getting compiler errors in Unity that say things like "Wrangler does not contain definition for "halfWidth" when I clearly define halfWidth = 6.
What do I do to add the definition and fix these errors?
C# is case sensitive.
In Critter code you use:
float startX = Random.Range (-Wrangler.halfWidth, Wrangler.halfwidth);
float startY = Random.Range (-Wrangler.halfHeight, Wrangler.halfheight);
In Wrangler, halfWidth and halfWidth are declared with capital W and H, but here you use halfwidth and halfheight.
About the error in the image - the Wrangler as you posted is correct and should compile, however pay attention that the error is about "CrabPrefab" and not "crabPrefab" as you declared it, so maybe in your local code you used capital CrabPrefab and CritterPrefab instead?
Just pay attention to the case of your variables.

How can I reset a Vector3 position?

I'm attempting to write a script that instantiates a row of prefabs. This worked the first time, but every time after that it instantiates one at 0x,0y,0z and the rest ~30x-40x away. I tried setting the initial position before the for loop executes and then resetting the position using the initalPos variable, but that doesn't seem to work. in my code
public class generator : MonoBehaviour {
public int height = 0;
public int width = 0;
private Vector3 temp;
public GameObject sprite;
private Vector3 initialPos;
void Start ()
{
initialPos = new Vector3(0,0,0);
for(int i = 0; i < width; i++)
{
Instantiate (sprite, temp, Quaternion.identity);
temp = sprite.transform.position;
temp.x += 0.089f;
sprite.transform.position = temp;
}
temp = initialPos;
}
}
The temp variable is what I'm setting the current position to so I can add 0.089 to it so that my sprites will line up. I'm trying to reset that value so that they line up starting at 0x every time.
You can simplify your code by specifying the number of prefabs you wish to spawn in a row, rather than the exact width.
Additionally, instead of incrementing your spawn position x value by a hard coded number, use gameObject.transform.localScale.x instead.
For example:
public GameObject Cube;
void Start()
{
SpawnRow(Vector3.zero, 10);
}
void SpawnRow(Vector3 startPosition, int RowLength)
{
Vector3 currentPos = startPosition;
for (int i = 0; i < RowLength; i++)
{
Instantiate(Cube, currentPos, Quaternion.identity);
currentPos.x += Cube.transform.localScale.x;
}
}
Additionally, if you wanted to do something like spawn additional rows next to your each other, you could call SpawnRow() like this:
void Start()
{
Vector3 currentPos = Vector3.zero;
for (int i = 0; i < 3; i++)
{
SpawnRow(currentPos, 10);
currentPos.z += Cube.transform.localScale.z;
}
}
This would give you three rows of 10 gameObjects directly next to each other.

Categories