Hello there everybody!
I'm trying to create a variable where you can put the number of points and the radius of a circle, and it will divide those points uniformly around the circle.
I'm trying to not use the Euler angles to set rotation or the Rotate or RotateAroundmethods.
But I am not having success...
These are how my code looks until the moment
public class PowerUps : MonoBehaviour
{
public GameObject PowerUpPrefab;
public GameObject Player;
public int PowerUpCount = 3;
public float PowerUpRadius = 1;
public Vector3 newPowerupSpace;
public GameObject[] SpawnPowerUps()
{
//get player positin
Vector3 anchorPoint = Player.transform.position;
GameObject[] SpawnPowerUps = new GameObject[PowerUpCount];
float angleStep = Mathf.HalfToFloat((ushort)(360.0 / PowerUpCount));
for (int i = 0; i < PowerUpCount; i++)
{
float theta = i * angleStep;
newPowerupSpace.x = anchorPoint.x + (PowerUpRadius * Mathf.Cos(theta));
newPowerupSpace.y = anchorPoint.y + (PowerUpRadius * Mathf.Sin(theta));
SpawnPowerUps[i] = (GameObject)Instantiate(PowerUpPrefab, newPowerupSpace, Quaternion.identity);
}
return null;
}
}
Any suggestions?
Update:
I changed the
float angleStep = Mathf.HalfToFloat((ushort)(360.0 / PowerUpCount));
to
float angleStep = ((ushort)((360.0 / PowerUpCount) * Mathf.Deg2Rad));
and now is working.
I feel kind of stupid....
Update 2:
After doing some tests, I notice that some numbers don't divide uniformly across the circle. That's because I was converting de circle degrees to radium in the wrong part.
Here's how the code looks like now:
public GameObject[] SpawnPowerUps()
{
Vector3 anchorPoint = Player.transform.position;
GameObject[] SpawnPowerUps = new GameObject[PowerUpCount];
float angleStep = ((ushort)(360.0 / PowerUpCount));
for (int i = 0; i < PowerUpCount; i++)
{
float theta = i * angleStep;
newPowerupSpace.x = anchorPoint.x + (PowerUpRadius * Mathf.Cos(theta * Mathf.Deg2Rad));
newPowerupSpace.y = anchorPoint.y + (PowerUpRadius * Mathf.Sin(theta * Mathf.Deg2Rad));
SpawnPowerUps[i] = (GameObject)Instantiate(PowerUpPrefab, newPowerupSpace, Quaternion.identity);
}
return null;
}
}
To describe a circle in 3D it should have three parameters, Position, Radius and Normal. The normal can be used to create a transform from the unit circle to the 3D space. This example uses instance methods instead of static methods for cross/dot/normalization, but it should be trivial to translate to the unity3D conventions.
First we create an arbitrary vector that is orthogonal to the normal:
public static Vector3 GetOrthogonal(this Vector3 self)
{
self = self.Normalized();
if (Math.Abs(self.Dot(Vector3.UnitX)) > 0.9)
{
return self.Cross(Vector3.UnitY).Normalized();
}
return self.Cross(Vector3.UnitX).Normalized();
}
We can then create two vectors that are orthogonal to the normal, and to each other:
var xAxis = normal.GetOrthogonal();
var yAxis = xAxis.Cross(normal).Normalized();
Getting the points is then simply multiplying each coordinate with the corresponding axis:
var x = xAxis * (PowerUpRadius * Mathf.Cos(theta * Mathf.Deg2Rad));
var y = yAxis * (PowerUpRadius * Mathf.Sin(theta * Mathf.Deg2Rad));
newPowerupSpace = x + y + anchorPoint;
You could do the same by producing a transform matrix and transforming the 2D points. But this is fairly simple to do.
Related
An example of my code, lets say I have a solar system, center being the sun, this code will spawn all the planets around it randomly with a set radius for each planet, in the Vector3 I change and randomize the X and Z, leaving Y at 0.
This is the code, planetInt is the number of planet in the for loop, 0 being first planet up to the 8 planet.
private float orbitRadius = 3150f;
// Get Radius
float newOrbitRadius = orbitRadius * (planetInt + 1);
// Get Random Positon (here)
Vector3 randomPlanetPosition = new Vector3(Random.insideUnitSphere.x * newOrbitRadius, 0, Random.insideUnitSphere.z * newOrbitRadius);
How would I change the Vector3( X and Z) to be in a specific zone, for example I want all the positions to be in the same area like this picture.
How do specify a specific angle like the multiple ones in this image, with Y being 0.
Here is an example:
I created this script:
(I suppose your sun is at the center 0;0;0)
public class NewBehaviourScript : MonoBehaviour
{
public GameObject PlanetPrefab = null;
public int numPlanets = 20;
public float orbitLengthBase = 0.3f;
public float angleMin = 45f;
public float angleMax = 120f;
void Start()
{
for (int i = 0; i < numPlanets; i++)
{
var newPlanet = Instantiate(PlanetPrefab);
var randomRotationAngle = Random.Range(angleMin, angleMax);
var rotation = Quaternion.Euler(0, randomRotationAngle, 0);
newPlanet.transform.position = rotation * Vector3.right * orbitLengthBase * i;
}
}
}
I always assign an arbitrary base vector (here: pointing to the right, (1;0;0)) as the position of the new planet, I multiply it by some factor the change the distance from sun just for the example.
More importantly, I multiply it on the left by a quaternion (math thing to represent a rotation), that I just created with a random Y angle between the max and the min that you want.
You can play with some parameters like angleMax and angleMin to see by yourself.
I am generating small spheres between two given points. However it always generates in a straight line. Is it possible that the spheres can start generating as a curve from Point A to Point B?
I am defining how many spheres should be generated between two points by defining NumberOfSegments.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GeneratePoints : MonoBehaviour
{
public Transform PointA;
public Transform PointB;
public float NumberOfSegments = 3;
public float AlongThePath = .25f;
// Update is called once per frame
void Start()
{
StartCoroutine(StartSpheringOut());
}
IEnumerator StartSpheringOut()
{
NumberOfSegments += 1;// since we are skipping 1st placement since its the same as starting point we increase the number by 1
AlongThePath = 1 / (NumberOfSegments);//% along the path
for (int i = 1; i < NumberOfSegments; i++)
{
yield return new WaitForSeconds(0.05f);
Vector3 CreatPosition = PointA.position + (PointB.position - PointA.position) * (AlongThePath * i);
GameObject sphere = GameObject.CreatePrimitive(PrimitiveType.Sphere);
sphere.transform.position = CreatPosition;
sphere.transform.localScale = new Vector3(0.05f, 0.05f, 0.05f);
}
}
}
This actually almost exists already in the Unity Manual:
using UnityEngine;
public class CircleFormation : MonoBehaviour
{
// Instantiates prefabs in a circle formation
public GameObject prefab;
public int numberOfObjects = 20;
public float radius = 5f;
void Start()
{
for (int i = 0; i < numberOfObjects; i++)
{
float angle = i * Mathf.PI * 2 / numberOfObjects;
float x = Mathf.Cos(angle) * radius;
float z = Mathf.Sin(angle) * radius;
Vector3 pos = transform.position + new Vector3(x, 0, z);
float angleDegrees = -angle*Mathf.Rad2Deg;
Quaternion rot = Quaternion.Euler(0, angleDegrees, 0);
Instantiate(prefab, pos, rot);
}
}
}
You can use this for starters and then adjust it to your needs in order to make it work in general. So since you only want the half of the circle all you have to do is basically to devide the angle by 2 and add the pointA and pointB in order to calculate the center position. Also if both positions are not in the same XZ-plane you would have to rotate the entire circle:
public GameObject A;
public GameObject B;
public int amount;
[ContextMenu("PlaceSpheres()")]
privtae void DebugPlace()
{
PlaceSpheres(A.transform.position, B.transform.position, amount);
}
public void PlaceSpheres(Vector3 posA, Vector3 posB, int numberOfObjects)
{
// get circle center and radius
var radius = Vector3.Distance(posA, posB) / 2f;
var centerPos = (posA + posB) / 2f;
// get a rotation that looks in the direction
// posA -> posB
var centerDirection = Quaternion.LookRotation((posB - posA).normalized);
for (var i = 0; i < numberOfObjects; i++)
{
// Max angle is 180° (= Mathf.PI in rad) not 360° (= Mathf.PI * 2 in rad)
// |
// | don't place the first object exactly on posA
// | but start already with an offset
// | (remove the +1 if you want to start at posA instead)
// | |
// | | don't place the last object on posB
// | | but end one offset before
// | | (remove the +1 if you want to end
// | | exactly a posB instead)
// | | |
// V V V
var angle = Mathf.PI * (i + 1) / (numberOfObjects + 1f);
var x = Mathf.Sin(angle) * radius;
var z = Mathf.Cos(angle) * radius;
var pos = new Vector3(x, 0, z);
// Rotate the pos vector according to the centerDirection
pos = centerDirection * pos;
var sphere = GameObject.CreatePrimitive(PrimitiveType.Sphere);
sphere.transform.position = centerPos + pos;
sphere.transform.localScale = new Vector3(0.05f, 0.05f, 0.05f);
}
}
So I have this function attached to a sphere and in inverse one attached to another. They are rigged to emit a trail and the game object they are orbiting around is a sphere which is locked at 0,0,0. This is my code so far :
float t = 0;
float speed;
float width;
float height;
int cat = 0;
public GameObject orbit; //the object to orbit
// Use this for initialization
void Start()
{
speed = 2;
width = 5;
height = 2;
InvokeRepeating("test", .001f, .009f);
}
void test()
{
t += Time.deltaTime * speed;
Vector3 orbitv = orbit.transform.position;
float inc = .0000000001f;
inc += inc + t;
float angmom = .00001f;
angmom = angmom + t;
float x = orbitv.x + Mathf.Cos(t);
float z = orbitv.z + inc; //+ (Mathf.Cos(t)*Mathf.Sin(t));
float y = orbitv.y + Mathf.Sin(t);
transform.position = new Vector3(x, y, z);
}}
Which does this:
Instead of moving in the z direction exclusively, I'd like them to continue their current rotation, but in a circle around the sphere at 0,0,0 while staying at the same y level. Anyone have any ideas how I can do this?
Edit: This is what I'm trying to do:
Here's some code I cooked up for you.
All the movement is achieved with basic trigonometric functions and some easy vector math.
To tackle problems like these, try to break things down like I did with dividing the movement into circular/up-down & sideways. This lets you create the ring effect.
Adding more intertwined waves should be trivial by changing the phase of the oscillations and adding more trails.
public class Orbit : MonoBehaviour {
float t = 0;
public float speed;
public float width;
public float height;
public float radius;
int cat = 0;
public GameObject center; //the object to orbit
public Vector3 offset;
void Update()
{
t = Time.time * speed;
OrbitAround();
AddSideways();
}
void OrbitAround()
{
float x = radius * Mathf.Cos(t);
float y = Mathf.Sin(offset.y * t);
float z = radius * Mathf.Sin(t);
transform.position = new Vector3(x, y, z);
}
void AddSideways()
{
float x = Mathf.Cos(offset.x * t);
Vector3 dir = transform.position - center.transform.position;
transform.position += dir.normalized * x;
}
}
It should give you a trail like this:
You can play around with the Vec3 offset which changes the frequency of the oscillations and essentially lets you choose the number of rings.
The Circle part put the objects in the air.
How can i make that they will be on the ground ?
They are standing in the air. When using the square formation they are on ground but with the circle they are in the air.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SquadFormation : MonoBehaviour
{
enum Formation
{
Square, Circle
}
public Transform squadMemeber;
public int columns = 4;
public int space = 10;
public int numObjects = 20;
// Use this for initialization
void Start()
{
ChangeFormation();
}
// Update is called once per frame
void Update()
{
}
private void ChangeFormation()
{
Formation formation = Formation.Square;
switch (formation)
{
/*case Formation.Square:
for (int i = 0; i < 23; i++)
{
Transform go = Instantiate(squadMemeber);
Vector3 pos = FormationSquare(i);
go.position = new Vector3(transform.position.x + pos.x, 0, transform.position.y + pos.y);
go.Rotate(new Vector3(0, -90, 0));
}
break;*/
case Formation.Circle:
Vector3 center = transform.position;
for (int i = 0; i < numObjects; i++)
{
Vector3 pos = RandomCircle(center, 5.0f);
var rot = Quaternion.LookRotation(pos - center);
Instantiate(squadMemeber, pos, rot);
}
break;
}
}
Vector2 FormationSquare(int index) // call this func for all your objects
{
float posX = (index % columns) * space;
float posY = (index / columns) * space;
return new Vector2(posX, posY);
}
Vector3 RandomCircle(Vector3 center, float radius)
{
float ang = Random.value * 360;
Vector3 pos;
pos.x = center.x + radius * Mathf.Sin(ang * Mathf.Deg2Rad);
pos.z = center.z + radius * Mathf.Cos(ang * Mathf.Deg2Rad);
pos.y = center.y;
return pos;
}
}
They should be instantiating on the ground(Terrain).
Need to position them on the ground.
Update:
This is what i tried now.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SquadFormation : MonoBehaviour
{
enum Formation
{
Square, Circle
}
public Transform squadMemeber;
public int columns = 4;
public int space = 10;
public int numObjects = 20;
public float yOffset = 1;
// Use this for initialization
void Start()
{
ChangeFormation();
}
// Update is called once per frame
void Update()
{
}
private void ChangeFormation()
{
Formation formation = Formation.Circle;
switch (formation)
{
/*case Formation.Square:
for (int i = 0; i < 23; i++)
{
Transform go = Instantiate(squadMemeber);
Vector3 pos = FormationSquare(i);
go.position = new Vector3(transform.position.x + pos.x, 0, transform.position.y + pos.y);
go.Rotate(new Vector3(0, -90, 0));
}
break;*/
case Formation.Circle:
Vector3 center = transform.position;
for (int i = 0; i < numObjects; i++)
{
Vector3 pos = RandomCircle(center, 5.0f);
var rot = Quaternion.LookRotation(pos - center);
pos.y = Terrain.activeTerrain.SampleHeight(pos);
pos.y = pos.y + yOffset;
Instantiate(squadMemeber, pos, rot);
}
break;
}
}
Vector2 FormationSquare(int index) // call this func for all your objects
{
float posX = (index % columns) * space;
float posY = (index / columns) * space;
return new Vector2(posX, posY);
}
Vector3 RandomCircle(Vector3 center, float radius)
{
float ang = Random.value * 360;
Vector3 pos;
pos.x = center.x + radius * Mathf.Sin(ang * Mathf.Deg2Rad);
pos.z = center.z + radius * Mathf.Cos(ang * Mathf.Deg2Rad);
pos.y = center.y;
return pos;
}
}
I added a offset yOffset and this two lines inside the for loop:
pos.y = Terrain.activeTerrain.SampleHeight(pos);
pos.y = pos.y + yOffset;
Now they are on the ground but lie down on the back/stomach and not standing like they were in the air.
Why when instantiating objects they are in the air and not on ground?
You need to find a way to calculate the height of the terrain then use that as your y-axis value.
After you get your pos, modify the y-axis with the Terrain.activeTerrain.SampleHeight function.
Vector3 pos = RandomCircle(center, 5.0f);
pos.y = Terrain.activeTerrain.SampleHeight(pos);
Maybe add an offset depending on the type of the GameObject(The yOffset should be a float)
pos.y = pos.y + yOffset;
Now, go ahead and instantiate with that new pos:
var rot = Quaternion.LookRotation(pos - center);
Instantiate(squadMemeber, pos, rot);
Note:
Depending on the size of your character,you must and have to keep changing the value of the yOffset until you get the position you want. This position should work well for the-same character. If you want to do this to the GameObjects with different size, you also have to modify the yOffset until you are satisfied with it.
Not an actual answer, but some advice:
I see some basic problem here with your code being kind of confused anyway. There are some things that should be similar that aren't. I think the baseline here is that you need some basic structure.
First things first, you should start with identifying the basic parts you need here.
There are three basic things to resolve:
XY-position
Z-position
rotation
Last one is the issue with your characters lying on the ground. See the rotation by -90 degrees for the square? I assume that this one does this.
But a more pressing matter here, here is a question for you: Aside from the XY-position, what should be the difference between squares and circles? Answer: none. You treat both cases far more different than they should be. The code should look somewhat like this:
Position xyPosition;
switch (formation)
case Circle: xyPosition = randomCirclePosition(someargument)
case Square: xyPosition = randomSquarePosition(someargument)
Position position = rotateUpright(xyPosition)
position = adjustZ(position)
See how the difference is applied only at one part?
The thing that jumps to my eyes immediately are those signatures: Vector2 FormationSquare and Vector3 RandomCircle. In particular, Vector2 and Vector3. Those are two entirely different approaches. Why? This makes your code unnecessarily heterogeneous, makes it harder to read. Does not allow for the clear structure I wrote above. Why did you derive that much? Why didn't you copy the old code of the square and then adjusted it as needed?
Do I assume correct when I say that you copied the square code from the internet? And that you have troubles understanding it for some reason? Maybe because you are not familiar with the math behind it? If so, my "answer" is this: Take the square code and do not continue until you understand any single line it it. What it does, why it is needed, what happens without it. Perfectly fine if you simply go and outcomment lines, then look what happens in the engine. Or ask somebody. But it is most important that you understand every single line in the end.
You also might want to put the code on CodeReview when it is working. I see some issues here and there in style. Like the mysterious 23 that comes out of nothing.
Sorry if this sounded a little headstrong or the like, or if I misjudge the situation here.
So I have been working on a piece of code that generates a perlin noise texture and applies it to a plane in order to create waves. But I can't get it to set the heightmap texture of the material. I have included material.EnableKeyword("_PARALLAXMAP"); but it does nothing. I have tried this with the normal map as well, without results. Here's the full code.
using UnityEngine;
using System.Collections;
public class NoiseGenerator : MonoBehaviour {
private Texture2D noiseTex;
private float x = 0.0F;
private float y = 0.0F;
public int scale = 10;
private Color[] pixels;
public float speed;
public float move = 0.0F;
void Start () {
Renderer render = GetComponent<Renderer>();
noiseTex = new Texture2D(scale,scale);
render.material = new Material(Shader.Find("Standard"));
render.material.EnableKeyword("_PARALLAXMAP");
render.material.SetTexture("_PARALLAXMAP", noiseTex);
pixels = new Color[noiseTex.width * noiseTex.height];
}
void Update () {
float y = 0.0F;
while (y < noiseTex.height)
{
float x = 0.0F;
while (x < noiseTex.width)
{
float xCoord = move + x / noiseTex.width * scale;
float yCoord = move + y / noiseTex.height * scale;
float sample = Mathf.PerlinNoise(xCoord, yCoord);
pixels[Mathf.RoundToInt(y) * noiseTex.width + Mathf.RoundToInt(x)] = new Color(sample, sample, sample);
x++;
}
y++;
}
noiseTex.SetPixels(pixels);
noiseTex.Apply();
move = move + speed;
}
}
You need to include a Material that use this Parallax variant to notify Unity about you need this.
This can be used in the scene or include in the resource folder.
If not Unity omit this on build how unused.
Just use
ur_material.SetFloat("_Parallax",[value])