I have just done the Curves and Splines Tutorials from the catlikecoding.com website (http://catlikecoding.com/unity/tutorials/curves-and-splines/) and after finishing it I came up with an idea. I would like to spawn the items from the SplineDecorator at the same distance from the spline at both sides of the spline. I have tried duplicating the for loop and instantiating the newItem twice but at different position but it doenst work as I want to. Here's the Spline Decorator script that takes a spline and then Instantiate some items along the its path
using UnityEngine;
public class SplineDecorator : MonoBehaviour
{
public BezierSpline spline;
public int frequency;
public float distanceBetweenItems;
public bool lookForward;
public Transform[] items;
private void Awake()
{
if (frequency <= 0 || items == null || items.Length == 0)
{
return;
}
float stepSize = 1f / (frequency * items.Length);
for (int p = 0, f = 0; f < frequency; f++)
{
for (int i = 0; i < items.Length; i++, p++)
{
Transform item = Instantiate(items[i]) as Transform;
Vector3 position = spline.GetPoint(p * stepSize);
item.transform.localPosition = new Vector3(position.x + distanceBetweenItems, position.y, position.z);
if (lookForward)
{
item.transform.LookAt(position + spline.GetDirection(p * stepSize));
}
item.transform.parent = transform;
}
}
}
}
You don't define exactly what "at both sides" means to you, so I'm not sure if this is what you're looking for, but maybe this will get you on the right track.
I replaced the inner loop with the following and got a sort of "race track" feel with distanceBetweenItems = 2:
Transform item = Instantiate(items[i]) as Transform;
Transform item2 = Instantiate(items[i]) as Transform;
Vector3 position = spline.GetPoint(p * stepSize);
Vector3 direction = spline.GetDirection(p * stepSize);
Vector3 cross = Vector3.Cross(direction, Vector3.up);
Vector3 delta = cross.normalized * (distanceBetweenItems/2);
item.transform.localPosition = new Vector3(position.x + delta.x, position.y + delta.y, position.z + delta.z);
item2.transform.localPosition = new Vector3(position.x - delta.x, position.y - delta.y, position.z - delta.z);
if (lookForward)
{
item.transform.LookAt( position + spline.GetDirection(p * stepSize));
item2.transform.LookAt(position + spline.GetDirection(p * stepSize));
}
item.transform.parent = transform;
item2.transform.parent = transform;
What I've done here is use Vector3.Cross() (Unity Documentation) to find the line perpendicular to both the spline and Vector3.up. Then I calculate how far along that line to go by normalizing it and multiplying it by half of distanceBetweenItems. (So that, combined, they're distanceBetweenItems apart.)
If you instead wanted something like tracing the wingtips of a plane flying that spline, you'll need to replace the Vector.up above. The simplest way is to replace it with the 'binormal vector', though there are issues with that. This pdf I just found talks a little about it, and might get you on the right path.
(Goofing around, I was able to get a reasonable approximation by replacing the Vector3 cross = ... line with Vector3 cross = Vector3.Cross(direction, direction - spline.GetDirection(p * stepSize + 0.01f));, though it's screwy on sharp bends, non-mirrored vertices, and at the start/end loop.)
Related
I'm new to godot 3.4! My first project is a orthographic chess make. I cant generate the chess board.
I've wrote the following script trying to generate the chessboard:
using Godot;
using System;
public class TileSpawner : Spatial {
[Export] private readonly Material lightSquare;
[Export] private readonly Material darkSquare;
private bool isLight = false;
public override void _Ready() {
Vector2 coordinates = new Vector2(0, 0);
for (int y = 0; y < 8; y++)
for (int x = 0; x < 8; x++) {
coordinates.x = x; coordinates.y = y;
AddChild(MakeTile(coordinates));
}
}
private MeshInstance MakeTile(Vector2 coordinates) {
MeshInstance mesh = new MeshInstance() {
Mesh = new CubeMesh() {
Material = isLight ? lightSquare : darkSquare
}
};
mesh.Translate(new Vector3(-coordinates.x, 0, coordinates.y) + new Vector3(coordinates.x/2, 0, -coordinates.y/2));
mesh.Scale = new Vector3(1.0f, 0.2f, 1.0f);
isLight = !isLight;
return mesh;
}
}
and the result its flickery...
one object overlapping other
so I added this line on the MakeTile function to see the problem clearly:
mesh.Translate(isLight ? Vector3.Up : Vector3.Down);
and got something like this.there's two big block in place small 64 tiles
not sure what's going on. I've been trying to solve this for 4 hours.
#Theraot answer was promising but didn't changed any thing.
My Solution:
Step 1: Create a scene containing the dark and light square mesh.
Step 2: Go Scene(On the top left corner of the screen) > Convert To... > MeshLibrary.
Step 3: Tick the "Merge With Existing" Checkbox and save the file as name.meshlib
Step 4: Create a GridMap on the scene you want your chessboard to be in.
Step 5: Load the name.meshlib on the GridMap Node and attach the following script:
using Godot;
using System;
public class FillWithTile : GridMap {
public override void _Ready() {
Vector2 coordinates = new Vector2(0, 0);
for (int y = -4; y < 4; y++)
for (int x = -4; x < 4; x++) {
coordinates.x = x; coordinates.y = y;
MakeTile(coordinates);
}
}
private void MakeTile(Vector2 coordinates) {
SetCellItem((int)coordinates.x, 0, (int)coordinates.y, (coordinates.x % 2 == 0) != (coordinates.y % 2 == 0)?1:0);
}
}
We are going to make a Transform:
var t = new Transform(
x_axis,
y_axis,
z_axis,
origin
);
So, let us define the components we used there. First for origin I'm going to guess that leaving it zero is fine. You will be able to change it to move the board.
var origin = Vector3.Zero;
And I'm also assuming the axis can be in the usual directions, just scaled:
var x_axis = Vector3.Right;
var y_axis = Vector3.Up * 2.0f;
var z_axis = Vector3.Back;
Alternatively, do you want them rotated? You could do something like this:
var x_axis = (Vector3.Right + Vector3.Back).Normalized();
var y_axis = Vector3.Up * 2.0f;
var z_axis = (Vector3.Back + Vector3.Left).Normalized();
Addendum: The scale is too big. The tiles are taking up the area of four cells. So shrink on the x and z axis by half:
var x_axis = Vector3.Right * 0.5f;
var y_axis = Vector3.Up * 2.0f;
var z_axis = Vector3.Back * 0.5f;
Or for the other case:
var x_axis = (Vector3.Right + Vector3.Back).Normalized() * 0.5f;
var y_axis = Vector3.Up * 2.0f;
var z_axis = (Vector3.Back + Vector3.Left).Normalized() * 0.5f;
Now, do you remember I said this is the Transform?
var t = new Transform(
x_axis,
y_axis,
z_axis,
origin
);
Well, not quite. We are going to displace it according to the coordinates we got, like this:
var t = new Transform(
x_axis,
y_axis,
z_axis,
origin + x_axis * coordinates.x + z_axis * coordinates.y
);
Addendum: I think you need to compensate for halving the scale by doubling the offset here: origin + x_axis * coordinates.x * 2.0f + z_axis * coordinates.y * 2.0f
And set it to the mesh:
mesh.Transform = t;
Addendum: the decision of light vs dark si wrong. It goes by rows and switching, like this:
10101010
10101010
…
Here is an alternative algorithm:
isLight = (((int)coordinates.x) % 2 == 0) != (((int)coordinates.y) % 2 == 0)
I have followed a YouTube tutorial on how to make a doodle jump replica in 5 minutes.
The problem is, this tutorial is rushed and does not provide all the information needed and thus I have encountered one big problem.
The infinite random platform generation keeps skewing off to either the left or right side of the screen after a certain point, and I have no clue as to why.
Here is the video tutorial: https://www.youtube.com/watch?v=IUzI95mmbwA
I am just wondering if anyone on here could be kind enough to help me on this small problem as it is for my school project and I am not sure how to fix it.
GameManager:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GameManager : MonoBehaviour
{
public GameObject platformPrefab;
public int platformCount = 300;
// Use this for initialization
void Start ()
{
Vector3 spawnPosition = new Vector3();
for (int i = 0; i < platformCount; i++)
{
spawnPosition.y += Random.Range(.5f, 2f);
spawnPosition.x += Random.Range(-5f, 5f);
Instantiate(platformPrefab, spawnPosition, Quaternion.identity);
}
}
}
Platforms being close together issue
So you generate your platforms using
void Start ()
{
Vector3 spawnPosition = new Vector3();
for (int i = 0; i < platformCount; i++)
{
spawnPosition.y += Random.Range(.5f, 2f);
spawnPosition.x += Random.Range(-5f, 5f);
Instantiate(platformPrefab, spawnPosition, Quaternion.identity);
}
}
which basically means that the next platform always has an offset to the one before in a range +/- 5.
This means however, that the next value would always have to counterbalance the current offset in order to be fully balanced in total. Since you use a random and not an equal distribution thiis is not guaranteed!
In order to make sure platforms don't get "skewed" to one side infinitely I would simply make sure they still "fit into the screen" and clamp the position accordingly like e.g.
public class GameManager : MonoBehaviour
{
public GameObject platformPrefab;
public int platformCount = 300;
[SerializeField] private Camera mainCamera;
// Adjust via the Inspector in order to also take platform width into account!
public float platformWidth;
[Min(0)] public float xRange = 5f;
float minX;
float maxX;
// Use this for initialization
void Start ()
{
if(!mainCamera) mainCamera = Camera.main;
// get the screen boarders in world space
minX = mainCamera.ScreenToWorldPoint(new Vector3(0, 0, -mainCamera.transform.position.z)).x + platformWidth / 2f;
maxX = mainCamera.ScreenToWorldPoint(new Vector3(Screen.width, 0, -mainCamera.transform.position.z)).x - platformWidth / 2f;
Vector3 spawnPosition = Vector3.zero;
for (int i = 0; i < platformCount; i++)
{
spawnPosition.y += Random.Range(.5f, 2f);
spawnPosition.x += Random.Range(-xRange, xRange);
// now simply clamp the x position so the platform
// doesn't go over the screen boarders
spawnPosition.x = Mathf.Clamp(spawnPosition.x, minX, maxX);
Instantiate(platformPrefab, spawnPosition, Quaternion.identity);
}
}
}
You could of course also instead of only clamping the position actively counter balance the range and shift it left or right accordingly like e.g.
Vector3 spawnPosition = Vector3.zero;
for (int i = 0; i < platformCount; i++)
{
spawnPosition.y += Random.Range(.5f, 2f);
var min = -5f;
var max = 5f;
var distanceToLeftBorder = spawnPosition.x - minX;
var distanceToRightBorder = maxX - spawnPosition.x;
if(distanceToLeftBorder < xRange)
{
min += xRange - distanceToLeftBorder;
max += xRange - distanceToLeftBorder;
}
else if(distanceToRightBorder < xRange)
{
min -= xRange - distanceToRightBorder;
max -= xRange - distanceToRightBorder;
}
spawnPosition.x += Random.Range(min, max);
// now simply clamp the x position so the platform
// doesn't go over the screen boarders
spawnPosition.x = Mathf.Clamp(spawnPosition.x, minX, maxX);
Instantiate(platformPrefab, spawnPosition, Quaternion.identity);
}
To your final issue the platforms being too close I would take yet another different approach:
Instead of randomly place at offset +/- 5 rather separate the sign from the distance and do
var sign = Mathf.Sign(Random.value);
var distance = Random.Range(platformWidth + SPACING, maxDistance);
spawnPosition.x += distance * sign;
which would make sure that the next platform is at minimum the platform width plus an optional spacing (both positive!) and not any closer to the current platform.
I would then instead of clamping simply check if you would hit the border and switch the sign like
var sign = Mathf.Sign(Random.value);
var distance = Random.Range(platformWidth + SPACING, maxDistance);
if(spawnPosition.x + distance * sign < minX || spawnPosition.x + distance * sign > maxX)
{
sign *= -1;
}
spawnPosition.x += distance * sign;
i have mathematical formula and i have spawn points. but, i have so differenet object sizes and radiuses. how i can instantiate a objects in the edge of circle and rotate them onto center circle like picture;
i am bad with this stuf and my code is wrong or not i dont know
```
public void GetObstacle(int count, CircleRotator rotator)
{
float TAU = 6.283185307179586f;
for (int i = 0; i < count; i++)
{
Transform cameraTR = mainCamera.transform;
Transform tr = rotator.transform;
var obj = new GameObject();
float t = (float)i / count;
float angle = t * TAU * 0.25f; //
Vector3 itemCenter = new Vector3(Mathf.Cos(angle) * rotator.GetRadius + tr.position.x,
tr.position.y + Mathf.Sin(angle) * rotator.GetRadius);
obj.transform.position = itemCenter;
Vector3 current = tr.position - cameraTR.position;
Vector3 target = obj.transform.position - cameraTR.position;
obj.transform.Rotate(Vector3.forward, Vector3.Dot(current, target));
obj.transform.SetParent(tr);
}
}
```
where 0.25 in here i dont understand. But without 0.25 all will broke. angle = t * TAU * 0.25f;
help pls or give some adwice. https://ibb.co/qNhZCh3 what i would like but what i have https://ibb.co/NFSRmSn
so many formulas and watched videos. but i can't understand how
Thanks #Ruzihm with help rotations.
spawn outside edge I got this way, where I'm not sure if this is the right way, where Size is the size of y collider. If someone show another good way i will be gratitude!
Vector3 direction = (tr.position - obj.transform.position);
obj.transform.rotation = Quaternion.LookRotation(Vector3.forward, direction);
obj.transform.position -= direction.normalized * obj.Size()/2f;
I am Programming a random "Stone" Spawner and have a big problem at the moment. I have some ideas how to fix it, but want to know a performance friendly way to do it.
So my way to Spawn the Objects on the Sphere Surface is this one:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SpawnObjects : MonoBehaviour
{
public Vector3 centerOfSphere = new Vector3(0, 5000, 0);
public float radiusOfSphere = 5000.0f;
public List<GameObject> stones;
public int stonesToSpawn = 1;
void Start()
{
Vector3 center = transform.position + centerOfSphere; //Setting the center position of the Sphere
//This for loop is spawning the Stones on a random position on Sphere Surface
for (int i = 0; i < stonesToSpawn; i++)
{
Vector3 pos = RandomCircle(center, radiusOfSphere);
Quaternion rot = Quaternion.FromToRotation(Vector3.forward, center - pos);
Instantiate(stones[Random.Range(0, stones.Count)], pos, rot);
}
}
//Method returns a Random Position on a Sphere
Vector3 RandomCircle(Vector3 center, float radius)
{
float alpha = UnityEngine.Random.value * 360;
float beta = UnityEngine.Random.value * 360;
Vector3 pos;
pos.x = radius * Mathf.Cos(beta) * Mathf.Cos(alpha);
pos.y = radius * Mathf.Cos(beta) * Mathf.Sin(alpha);
pos.z = radius * Mathf.Sin(beta);
return pos;
}
}
So thank you for your following explanations! :)
As said to make your live one step easier simply use Random.onUnitSphere so your entire method RandomCircle (change that name btw!) can be shrinked to
private Vector3 RandomOnSphere(Vector3 center, float radius)
{
return center + Random.onUnitSphere * radius;
}
And then in order to have a minimum distance between them there are probably multiple ways but I guess the simplest - brute force - way would be:
store already used positions
when you get a new random position check the distance to already existing ones
keep get new random positions until you have found one, that is not too close to an already existing one
This depends of course a lot on your use case and the amount of objects and the minimum distance etc - in other words I leave it up to you to assure that the requested amount and minimum distance is doable at all with the given sphere radius.
You could always leave an "emergency exit" and give up after e.g. 100 attempts.
Something like e.g.
// Linq offers some handy query shorthands that allow to shorten
// long foreach loops into single calls
using System.Linq;
...
private const int MAX_ATTEMPTS = 100;
public float minimumDistance = 1f;
void Start()
{
var center = transform.position + centerOfSphere;
// It is cheaper to use with square magnitudes
var minDistanceSqr = minimumDistance * minimumDistance;
// For storing the already used positions
// Already initialize with the correct capacity, this saves resources
var usedPositions = new List<Vector3>(stonesToSpawn);
for (int i = 0; i < stonesToSpawn; i++)
{
// Keep track of the attempts (for the emergency break)
var attempts = 0;
Vector3 pos = Vector3.zero;
do
{
// Get a new random position
pos = RandomOnSphere(center, radiusOfSphere);
// increase the attempts
attempts++;
// We couldn't find a "free" position within the 100 attempts :(
if(attempts >= MAX_ATTEMPTS)
{
throw new Exception ("Unable to find a free spot! :'(");
}
}
// As the name suggests this checks if any "p" in "usedPositions" is too close to the given "pos"
while(usedPositions.Any(p => (p - pos).sqrMagnitude <= minDistanceSqr)));
var rot = Quaternion.FromToRotation(Vector3.forward, center - pos);
Instantiate(stones[Random.Range(0, stones.Count)], pos, rot);
// Finally add this position to the used ones so the next iteration
// also checks against this position
usedPositions.Add(pos);
}
}
Where
usedPositions.Any(p => (p - pos).sqrMagnitude <= minDistanceSqr))
basically equals doing something like
private bool AnyPointTooClose(Vector3 pos, List<Vector3> usedPositions, float minDistanceSqr)
{
foreach(var p in usedPositions)
{
if((p - pos).sqrMagnitude <= minDistanceSqr)
{
return true;
}
}
return false;
}
if that's better to understand for you
using (unity 2019.3.7f1) 2d.
I have a player that moves around using a pullback mechanic and has a max power(like in Angry Birds).
I'm trying to draw a line(using a line renderer) that shows the exact path the player will go. I'm trying to make the line curve just like the player's path will. so far I've only managed to make a straight line in a pretty scuffed way.
The known variables are the Jump Power and the player's position, there is no friction. and I believe gravity is a constant(-9.81). Also, I would like to have a variable that allows me to control the line's length. And, if possible, the line will not go through objects and would act as if it has a collider.
// Edit
This is my current code. I changed The function so it would return the list's points because I wanted to be able to access it in Update() so it would only draw while I hold my mouse button.
My problem is that the trajectory line doesn't seem to curve, it goes in the right angle but it's straight. the line draws in the right direction and angle, but my initial issue of the line not curving remains unchanged. If you could please come back to me with an answer I would appreciate it.
enter code here
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TrajectoryShower : MonoBehaviour
{
LineRenderer lr;
public int Points;
public GameObject Player;
private float collisionCheckRadius = 0.1f;
public float TimeOfSimulation;
private void Awake()
{
lr = GetComponent<LineRenderer>();
lr.startColor = Color.white;
}
// Update is called once per frame
void Update()
{
if (Input.GetButton("Fire1"))
{
lr.positionCount = SimulateArc().Count;
for (int a = 0; a < lr.positionCount;a++)
{
lr.SetPosition(a, SimulateArc()[a]);
}
}
if (Input.GetButtonUp("Fire1"))
{
lr.positionCount = 0;
}
}
private List<Vector2> SimulateArc()
{
float simulateForDuration = TimeOfSimulation;
float simulationStep = 0.1f;//Will add a point every 0.1 secs.
int steps = (int)(simulateForDuration / simulationStep);
List<Vector2> lineRendererPoints = new List<Vector2>();
Vector2 calculatedPosition;
Vector2 directionVector = Player.GetComponent<DragAndShoot>().Direction;// The direction it should go
Vector2 launchPosition = transform.position;//Position where you launch from
float launchSpeed = 5f;//The initial power applied on the player
for (int i = 0; i < steps; ++i)
{
calculatedPosition = launchPosition + (directionVector * ( launchSpeed * i * simulationStep));
//Calculate gravity
calculatedPosition.y += Physics2D.gravity.y * (i * simulationStep);
lineRendererPoints.Add(calculatedPosition);
if (CheckForCollision(calculatedPosition))//if you hit something
{
break;//stop adding positions
}
}
return lineRendererPoints;
}
private bool CheckForCollision(Vector2 position)
{
Collider2D[] hits = Physics2D.OverlapCircleAll(position, collisionCheckRadius);
if (hits.Length > 0)
{
for (int x = 0;x < hits.Length;x++)
{
if (hits[x].tag != "Player" && hits[x].tag != "Floor")
{
return true;
}
}
}
return false;
}
}
Here's a simple way to visualize this.
To create your line you want a bunch of points.
The points represents the player's positions after being fired after X amount of time.
The position of each point is going to be : DirectionVector * (launch speed * time elapse) + (GravityDirection * time elapse^2)
You can decide in advance how far you pre calculate the points by simulating X duration and choosing the simulation step(calculate a point every X amount of time)
To detect collision each time you calculate a point you can do a small circle cast at that location. If it hits something you can stop add new points.
private float collisionCheckRadius = 0.1f;
private void SimulateArc()
{
float simulateForDuration = 5f;//simulate for 5 secs in the furture
float simulationStep = 0.1f;//Will add a point every 0.1 secs.
int steps = (int)(simulateForDuration/simulationStep);//50 in this example
List<Vector2> lineRendererPoints = new List<Vector2>();
Vector2 calculatedPosition;
Vector2 directionVector = new Vector2(0.5f,0.5f);//You plug you own direction here this is just an example
Vector2 launchPosition = Vector2.zero;//Position where you launch from
float launchSpeed = 10f;//Example speed per secs.
for(int i = 0; i < steps; ++i)
{
calculatedPosition = launchPosition + ( directionVector * (launchSpeed * i * simulationStep));
//Calculate gravity
calculatedPosition.y += Physics2D.gravity.y * ( i * simulationStep) * ( i * simulationStep);
lineRendererPoints.Add(calculatedPosition);
if(CheckForCollision(calculatedPosition))//if you hit something
{
break;//stop adding positions
}
}
//Assign all the positions to the line renderer.
}
private bool CheckForCollision(Vector2 position)
{
Collider2D[] hits = Physics2D.OverlapCircleAll(position, collisionCheckRadius);
if(hits.Length > 0)
{
//We hit something
//check if its a wall or seomthing
//if its a valid hit then return true
return true;
}
return false;
}
This is basically a sum of 2 vectors along the time.
You have your initial position (x0, y0), initial speed vector (x, y) and gravity vector (0, -9.81) being added along the time. You can build a function that gives you the position over time:
f(t) = (x0 + x*t, y0 + y*t - 9.81t²/2)
translating to Unity:
Vector2 positionInTime(float time, Vector2 initialPosition, Vector2 initialSpeed){
return initialPosition +
new Vector2(initialSpeed.x * t, initialSpeed.y * time - 4.905 * (time * time);
}
Now, choose a little delta time, say dt = 0.25.
Time | Position
0) 0.00 | f(0.00) = (x0, y0)
1) 0.25 | f(0.25) = (x1, y1)
2) 0.50 | f(0.50) = (x2, y2)
3) 0.75 | f(0.75) = (x3, y3)
4) 1.00 | f(1.00) = (x4, y4)
... | ...
Over time, you have a lot of points where the line will cross. Choose a time interval (say 3 seconds), evaluate all the points between 0 and 3 seconds (using f) and put your line renderer to cover one by one.
The line renderer have properties like width, width over time, color, etc. This is up to you.