I am trying to get two blocks to respawn vertically with a gap in-between. I have done this however, when I click play, one of the blocks width is too short. this results in having two gaps. how can I stop this from happening. I would only like the one gap which is between the two blocks. the code I am currently using:
public Transform block;
public Transform player;
private float objectSpawnedTo = 5.0f;
public static float distanceBetweenObjects = 9.5f;
private float nextCheck = 0.0f;
private ArrayList objects = new ArrayList();
void Start () {
maintenance(0.0f);
}
void Update () {
float playerX = player.position.y;
if(playerX > nextCheck)
{
maintenance(playerX);
}
}
private void maintenance(float playerX)
{
nextCheck = playerX + 30;
for (int i = objects.Count-1; i >= 0; i--)
{
Transform blck = (Transform)objects[i];
if(blck.position.y < (transform.position.y - 30))
{
Destroy(blck.gameObject);
objects.RemoveAt(i);
}
}
spawnObjects(5);
}
private void spawnObjects(int howMany)
{
float spawnX = objectSpawnedTo;
for(int i = 0; i<howMany; i++)
{
Vector3 pos = new Vector3(6.0f, spawnX, 0);
float firstRandom = Random.Range(1,8.6f);
Transform blck = (Transform)Instantiate(block, pos, Quaternion.identity);
blck.localScale+=new Vector3(firstRandom*2,0,0);
objects.Add(blck);
pos = new Vector3(-6.0f, spawnX, 0);
blck = (Transform)Instantiate(block, pos, Quaternion.identity);
blck.localScale +=new Vector3((8.6f-firstRandom)*2,0,0);
objects.Add(blck);
spawnX = spawnX + distanceBetweenObjects;
}
objectSpawnedTo = spawnX;
}
i have attached an image to show what i am trying to achieve. the red outline is the mobile screen. also i would like to be able to move the blocks either left or right using touch. how would i go about doing this.
Related
I have a method that is supposed to generate a certain number of Vector3 at a distance not less than specified.
// Generate random point based on plane area
public List<Vector3> GeneratePositions(int numberOfPositions, float minDistanceBetweenPositions)
{
float entireArea = 0f;
List<AreasWeight> areasWeights = new List<AreasWeight>();
List<Vector3> positions = new List<Vector3>();
foreach (GeneratorPlane plane in GeneratorPlanes.GetCollectionAsList())
{
entireArea += plane.GetArea();
}
foreach (GeneratorPlane plane in GeneratorPlanes.GetCollectionAsList())
{
float weight = plane.GetArea() / entireArea;
int numOfPositionsInArea = Mathf.RoundToInt(numberOfPositions * weight);
areasWeights.Add(new(plane, weight, numOfPositionsInArea));
}
foreach (AreasWeight areaWeight in areasWeights)
{
for (int i = 0; i < areaWeight.NumOfPointsInArea; i++)
{
Vector3 generatedPoint = areaWeight.Plane.GetRandomPointOnPlane();
foreach (Vector3 position in positions)
{
int attempts = 1;
while ((position - generatedPoint).magnitude < minDistanceBetweenPositions)
{
generatedPoint = areaWeight.Plane.GetRandomPointOnPlane();
attempts++;
if (attempts > 2000)
{
Debug.Log("Can't generate all positions.");
break;
}
}
}
positions.Add(generatedPoint);
}
}
return positions;
}
Get random point method:
public Vector3 GetRandomPointOnPlane()
{
float xPosition = Random.Range(Mathf.Min(DownPoint.x, DownPointHelper.x), Mathf.Max(DownPoint.x, DownPointHelper.x));
float zPosition = Random.Range(Mathf.Min(DownPoint.z, UpPointHelper.z), Mathf.Max(DownPoint.z, UpPointHelper.z));
return new(xPosition, DownPoint.y + 0.002f, zPosition);
}
But when i Instantiate objects based on these Vector3. Objects still have a distance less than the specified. What am i doing wrong?
I found a solution. The problem was a bad loop structure. When the algorithm confirmed that the distance was too small and generated a new one, it did not check whether the generated position had a gap from the previous positions on the list. It only confirmed that the gap was preserved and the program continued to execute.
I moved the code that makes sure that the distances are saved to the public List<Vector3> GeneratePositions(int numberOfPositions, float minDistanceBetweenPositions) method in the GeneratorPlane class. I also added a private Vector3 PickRandomPos() method to it, just to return the generated position.
Methods in the public class GeneratorPlane:
public Vector3 GetRandomPointOnPlane(List<Vector3> alreadyGeneratedPoints, float minDistnaceBetweenPositions)
{
if (alreadyGeneratedPoints.Count != 0)
{
int attemps = 1;
bool pointFound = false;
Vector3 posToReturn = new();
while (!pointFound)
{
pointFound = true;
posToReturn = PickRandomPos();
foreach (Vector3 position in alreadyGeneratedPoints)
{
if (Vector3.Distance(position, posToReturn) < minDistnaceBetweenPositions)
{
pointFound = false;
attemps++;
if (attemps > 2000)
{
Debug.LogError("Points cannot be generated. Too little available space");
return Vector3.zero;
}
break;
}
}
}
return posToReturn;
}
else
{
Debug.Log("First point generated");
return PickRandomPos();
}
}
private Vector3 PickRandomPos()
{
float xPosition = Random.Range(Mathf.Min(DownPoint.x, DownPointHelper.x), Mathf.Max(DownPoint.x, DownPointHelper.x));
float zPosition = Random.Range(Mathf.Min(DownPoint.z, UpPointHelper.z), Mathf.Max(DownPoint.z, UpPointHelper.z));
return new(xPosition, DownPoint.y + 0.002f, zPosition);
}
Method to generate and return a certain number of items:
public List<Vector3> GeneratePositions(int numberOfPositions, float minDistanceBetweenPositions)
{
float entireArea = 0f;
List<AreasWeight> areasWeights = new();
List<Vector3> positions = new();
foreach (GeneratorPlane plane in PlanesGenerator.GetCollectionAsList())
{
entireArea += plane.GetArea();
}
foreach (GeneratorPlane plane in PlanesGenerator.GetCollectionAsList())
{
float weight = plane.GetArea() / entireArea;
int numOfPositionsInArea = Mathf.RoundToInt(numberOfPositions * weight);
areasWeights.Add(new(plane, weight, numOfPositionsInArea));
}
foreach (AreasWeight areaWeight in areasWeights)
{
for (int i = 0; i < areaWeight.NumOfPointsInArea; i++)
{
Vector3 generatedPoint = areaWeight.Plane.GetRandomPointOnPlane(positions, minDistanceBetweenPositions);
positions.Add(generatedPoint);
}
}
return positions;
}
On the original code if you generate a point 2000 times you actually keep the last generatedPoint, and as you mentioned you don't actually cross check the whole list of positions, only the remaining positions.
Although you have solved your problem and posted a solution, I took the liberty of doing a simple script with the same end, I will share it here in hopes its useful for you or others.
This solution will not fill any area, its only making sure no two objects are at shorter distance than specified.
In my tests, with 50 nPoints only 10/20 points are instantiated before a point takes over 2000 attempts and consequently conclude the search for points. Although this will depend on the ratio between spawnLimits and nPoints.
[SerializeField]
GameObject trunkPrefab;
List<Vector3> positions;
//input variables
int nPoints = 50;
float minDistance = 2.5f;
int spawnLimits = 20;
void Start()
{
positions = new();
for (int i = 0; i < nPoints; i++)
{
Vector3 position = Vector3.zero;
bool newPosition = true;
int attempts = 0;
do
{
//first generation will be automatically added to the list
position = new(Random.Range(-spawnLimits, spawnLimits), .5f, Random.Range(-spawnLimits, spawnLimits));
if (positions.Count < 1)
{
break;
}
//every position will be compared here,
//if any position is too close from then new position
//"newPosition" is set to false and we try again from the start.
for (int p = 0; p < positions.Count; p++)
{
if (Vector3.Distance(position, positions[p]) < minDistance)
{
newPosition = false;
attempts++;
if (attempts > 2000)
{
Debug.Log("Max attempts reached.");
return;
}
break;
}
}
} while (!newPosition);
//adding a random rotation
Vector3 rotation = new(Random.Range(80, 100), Random.Range(0, 179), 0);
Instantiate(trunkPrefab, position, Quaternion.Euler(rotation));
positions.Add(position);
}
}
We are trying, with a friend, to modelise a tree on unity. To do so, we want to modelise a tree trunk. We are doing it with the following code :
`
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[ExecuteInEditMode]
public class Frustum : MonoBehaviour
{
/*====== PUBLIC ======*/
public float height = 1;
public float top_radius = 0.9F;
public float base_radius = 1F;
public int circle_subdivisions = 30;
/*====== PRIVATE ======*/
private Mesh mesh;
private Vector3[] vao;
private int[] ibo;
// Start is called before the first frame update
void Start()
{
mesh = new Mesh();
mesh.name = "Cone";
this.GetComponent<MeshFilter>().mesh = mesh;
draw();
}
// OnValidate is called at each public input change
private void OnValidate() {
draw();
}
private void draw() {
ComputeFrustumVao();
ComputeFrustumIbo();
mesh.Clear();
mesh.vertices = vao;
mesh.triangles = ibo;
mesh.RecalculateBounds();
mesh.RecalculateTangents();
mesh.RecalculateNormals();
// mesh.Optimize();
}
private void ComputeFrustumVao()
{
// COMPUTE BASE VERTICES
List<Vector3> vertices = ComputeCircleVertices(transform.position, base_radius, circle_subdivisions);
// ADD TOP VERTICES
Vector3 top_origin = transform.position + new Vector3(0, height, 0);
vertices.AddRange(ComputeCircleVertices(top_origin, top_radius, circle_subdivisions));
vao = vertices.ToArray();
}
private List<Vector3> ComputeCircleVertices(Vector3 origin, float radius, int nb_subdivisions) {
List<Vector3> vao = new List<Vector3>();
float step_angle = (2*Mathf.PI)/nb_subdivisions;
for(int i = 0; i < nb_subdivisions; ++i){
float current_angle = step_angle * i;
Vector3 translate_vector = new Vector3(
Mathf.Cos(current_angle) * radius, // x coordinate
0, // y coordinate
Mathf.Sin(current_angle) * radius // z coordinate
);
Vector3 new_vertex = origin + translate_vector;
vao.Add(new_vertex);
}
return vao;
}
private void ComputeFrustumIbo() {
List<int> indices = new List<int>();
int nb_vertices = vao.Length;
int nb_vertices_per_circle = nb_vertices/2;
int nb_triangles = nb_vertices_per_circle - 2;
// DRAW BASE
for(int i = 0; i < nb_triangles; ++i){
indices.Add(0);
indices.Add(i + 1);
indices.Add(i + 2);
}
// DRAW TOP
for(int i = nb_vertices_per_circle; i < nb_vertices_per_circle + nb_triangles; ++i){
indices.Add(nb_vertices_per_circle);
indices.Add(i + 2);
indices.Add(i + 1);
}
// DRAW SIDES
for(int i = 0; i < nb_vertices_per_circle; ++i){
int next_index = (i + 1) % nb_vertices_per_circle;
indices.Add(i); // BOTTOM LEFT
indices.Add( nb_vertices_per_circle + next_index); // TOP RIGHT
indices.Add(next_index); // BOTTOM RIGHT
indices.Add(i); // BOTTOM LEFT
indices.Add(i + nb_vertices_per_circle); // TOP LEFT
indices.Add(nb_vertices_per_circle + next_index); // TOP RIGHT
}
ibo = indices.ToArray();
}
void OnDrawGizmosSelected() //Draw the markers, if the object is selected
{
Gizmos.color = Color.red;
for (int i=0; i< mesh.vertices.Length; i++){
Gizmos.color= new Color(((float)i)/mesh.vertices.Length,0, 0);
Vector3 worldPos = transform.TransformPoint(mesh.vertices[i]);
Gizmos.DrawCube(worldPos, Vector3.one * 0.1f);
}
}
}
`
With this code we can create a 3D shape like a cube or a cylinder but when we create our top and our base ( in DRAW TOP and DRAW BASE) it makes the normal looks weird and I dont understand why. Can you help me to understand it please ?
our Normal problem
We know that ussually people create their models in 3D software like blender but we really need to do it in unity for our project.
I've seen that if my normals are pointing to the top the face are too illuminated but when they are facing down it's too shady. To create our base and top shape, we have followed this tutorial
The script below is intended to move the background with the character. However, the background does not move at all when using this code and I suspect the problem may be somewhere in the LateUpdate function as once that is changed to update, the background continuously moves on its own, but not with the player.
public class ParallaxController : MonoBehaviour
{
Transform cam; // Main Camera
Vector3 camStartPos;
Vector2 distance;
GameObject[] backgrounds;
Material[] mat;
float[] backSpeed;
float farthestBack;
[Range(0f,0.05f)]
public float parallaxSpeed;
void Start()
{
cam = Camera.main.transform;
camStartPos = cam.position;
int backCount = transform.childCount;
mat = new Material[backCount];
backSpeed = new float[backCount];
backgrounds = new GameObject[backCount];
for (int i = 0; i < backCount; i++)
{
backgrounds[i] = transform.GetChild(i).gameObject;
mat[i] = backgrounds[i].GetComponent<Renderer>().material;
}
BackSpeedCalculate(backCount);
}
void BackSpeedCalculate(int backCount)
{
for (int i = 0; i < backCount; i++)
{
if ((backgrounds[i].transform.position.z - cam.position.z) > farthestBack)
{
farthestBack = backgrounds[i].transform.position.z - cam.position.z;
}
}
for (int i = 0; i < backCount; i++)
{
backSpeed[i] = 1 - (backgrounds[i].transform.position.z - cam.position.z) / farthestBack;
}
}
void LateUpdate()
{
distance = cam.position - camStartPos;
transform.position = new Vector3(cam.position.x, transform.position.y, 0);
for (int i = 0; i < backgrounds.Length; i++)
{
float speedX = backSpeed[i] * parallaxSpeed;
float speedY = speedX / 2; // if you close Y movement , set to 0
mat[i].SetTextureOffset("_MainTex", new Vector2(distance.x*speedX, distance.y*speedY));
Debug.Log("for loop works!");
}
}
}
I dont know if I can call this algorithm. But I am working on a game in which player will move in a circular path.
As you can see in the picture player is suppose to orbit the circle. And obstacle shall be instantiated in the circle.I am trying to first create the obstacle in first half(left to the long cube) and then in the second half. But things are getting created in the next half too when code is not supposed to do that. Also, it is showing argument exception error. Please have a look at my code and tell me whether my method is wrong or my formulas are wrong or anything else.
public class ObjectInstantiater : MonoBehaviour {
DataHolder dataholder;
GameObject Obstacle;
LevelData leveldata;
private int currentlevel=0; // default level starts from 0
private List<GameObject> Inactivegameobject = new List<GameObject>(); // this object can be used
private List<GameObject> Activegameobject = new List<GameObject>();
private int totalgameobjects;
private int firsthalfgameobjects, secondhalfgameobjects;
public float outerradius;
public float innerradius;
private bool shallspawnouterradiues = true;
// Use this for initialization
void Awake () {
dataholder = (Object)GameObject.FindObjectOfType<DataHolder>() as DataHolder;
Obstacle = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
leveldata = dataholder.Leveldata[0];
}
void Start()
{
Updateleveldata();
FirstHalf();
}
public int Currentlevel
{
get { return currentlevel; }
set { currentlevel = value;
leveldata = dataholder.Leveldata[currentlevel];//sets the level data
}
}
private void Updateleveldata() // this function gets called after a round
{
totalgameobjects = Random.Range(leveldata.MinimumObstacle, leveldata.MaximumObstacle);
firsthalfgameobjects = Mathf.RoundToInt(totalgameobjects / 2);
secondhalfgameobjects = totalgameobjects - firsthalfgameobjects;
}
private void FirstHalf()
{
Debug.Log(firsthalfgameobjects);
Vector3 pos;
if (Inactivegameobject.Count < firsthalfgameobjects)
{
for (int x = 0; x <= (firsthalfgameobjects - Inactivegameobject.Count); x++)
{
GameObject obs = Instantiate(Obstacle) as GameObject;
obs.SetActive(false);
Inactivegameobject.Add(obs);
}
}
float spawnangledivision = 180 / firsthalfgameobjects;
float spawnangle = 180f;
for(int x = 0; x < firsthalfgameobjects; x++)
{
float proceduralRandomangle = spawnangle;
proceduralRandomangle = Random.Range(proceduralRandomangle , proceduralRandomangle + 2f);
if (shallspawnouterradiues)
{
pos = new Vector3(outerradius * Mathf.Cos(spawnangle), outerradius * Mathf.Sin(spawnangle), 0f);
shallspawnouterradiues = false;
}else
{
pos = new Vector3(innerradius * Mathf.Cos(spawnangle), innerradius * Mathf.Sin(spawnangle), 0f);
shallspawnouterradiues = true;
}
spawnangle += spawnangledivision;
Inactivegameobject[0].SetActive(true); // set it to 0
Inactivegameobject[0].transform.position = pos;
Activegameobject.Add(Inactivegameobject[0]);
Inactivegameobject.RemoveAt(0);
}
}
private void SecondHalf()// No need to check this
{
if (Inactivegameobject.Count < firsthalfgameobjects)
{
GameObject obs = Instantiate(Obstacle) as GameObject;
obs.SetActive(false);
Inactivegameobject.Add(obs);
}
}
}
In my code, I am attempting to move three sprites on the X axis at one direction and speed. However, when I wrote the code in my class, it compiled fine without errors, but when the game started, the sprites that I wanted to move do not move at all. They just sit there. Code below:
class Enemy : EnemySprite
{
const string ENEMY_ASSETNAME = "BadguyLeft";
const int START_POSITION_X1 = 350;
const int START_POSITION_X2 = 600;
const int START_POSITION_X3 = 750;
const int START_POSITION_Y = 415;
const int MOVE_LEFT = -1;
int WizardSpeed = 160;
enum State
{
Walking
}
The real issue is below:
public void LoadContent(ContentManager theContentManager)
{
base.LoadContent(theContentManager, ENEMY_ASSETNAME);
}
public void Update(GameTime theGameTime)
{
//KeyboardState aCurrentKeyboardState = Keyboard.GetState();
//UpdateMovement(aCurrentKeyboardState);
//mPreviousKeyboardState = aCurrentKeyboardState;
Position[0] = new Vector2(START_POSITION_X1, START_POSITION_Y);
Position[1] = new Vector2(START_POSITION_X2, START_POSITION_Y);
Position[2] = new Vector2(START_POSITION_X3, START_POSITION_Y);
base.Update(theGameTime, mSpeed, mDirection);
}
private void UpdateMovement(KeyboardState aCurrentKeyboardState)
{
//int positionTracker = START_POSITION_X3;
if (mCurrentState == State.Walking)
{
mSpeed = Vector2.Zero;
mDirection = Vector2.Zero;
for (int i = 0; i < Position.Count(); i++)
if (mCurrentState == State.Walking)
{
mSpeed.X = WizardSpeed;
mDirection.X = MOVE_LEFT;
}
}
You are simply never actually changing the position of the sprite.
You're update method is where the movement should (typically) take place.
It would look something like this:
//this will move an object to the left
Vector2 speed = new Vector2(-10, 0);
public void Update(GameTime theGameTime)
{
//this will add the speed of the sprite to its position
//making it move
Position[0] += speed;
Position[1] += speed;
Position[2] += speed;
base.Update(theGameTime, mSpeed, mDirection);
}