using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Teleport : MonoBehaviour {
public Vector3 terrainArea;
public float spinSpeed = 2.0f;
public int cloneTeleportations;
public GameObject prefab;
private bool rotate = false;
private bool exited = false;
private Transform[] teleportations;
private Random rnd = new Random();
private void Start()
{
GameObject go = GameObject.Find("Terrain");
Terrain terrain = go.GetComponent(Terrain);
terrainArea = terrain.terrainData.size;
for (int i = 0; i < cloneTeleportations; i++)
{
GameObject Teleportaion = Instantiate(prefab, new Vector3(Random.Range(i * 10.0F, i * 50.0F), 0, Random.Range(i * 10.0F, i * 50.0F)), Quaternion.identity);
Teleportaion.transform.parent = this.transform;
Teleportaion.transform.tag = "Teleportation";
}
}
}
Now some gameobjects are out of the terrain area.
What I want to do is to keep the clones to be in random position but only inside the terrain area.
How I do Instantiate Objects inside the terrain area?
I noticed that you are creating new instance of the Random class. Don't do that with Unity's Random API. Just use Random.Range to generate random numbers.
As for generating random GameObjects on a terrain:
1.Find the X position:
Min = terrain.terrainData.size.x.
Max = terrain.transform.position.x + terrain.terrainData.size.x.
The final random X should be:
randX = UnityEngine.Random.Range(Min, Min + Max);
2.Find the Z position:
Min = terrain.transform.position.z;
Max = terrain.transform.position.z + terrain.terrainData.size.z;
The final random Z should be:
randZ = UnityEngine.Random.Range(Min, Min + Max);
3.Find the Y position:
The y-axis does not have to be random. Although, you can make it a random value if you want.
It just has to be over the terrain so that the Instantiated Object will not be under the terrain.
This is where Terrain.SampleHeight comes into place. You must use this function in order to perfectly generate a position that is not under the terrain. You need the result from #1 and #2 to use this:
yVal = Terrain.activeTerrain.SampleHeight(new Vector3(randX, 0, randZ));
If you want to apply offset to the y-axis, you can now do it at this time.
yVal = yVal + yOffset;
Below is a general example. You have to use this example to extend your current code.
public GameObject prefab;
public Terrain terrain;
public float yOffset = 0.5f;
private float terrainWidth;
private float terrainLength;
private float xTerrainPos;
private float zTerrainPos;
void Start()
{
//Get terrain size
terrainWidth = terrain.terrainData.size.x;
terrainLength = terrain.terrainData.size.z;
//Get terrain position
xTerrainPos = terrain.transform.position.x;
zTerrainPos = terrain.transform.position.z;
generateObjectOnTerrain();
}
void generateObjectOnTerrain()
{
//Generate random x,z,y position on the terrain
float randX = UnityEngine.Random.Range(xTerrainPos, xTerrainPos + terrainWidth);
float randZ = UnityEngine.Random.Range(zTerrainPos, zTerrainPos + terrainLength);
float yVal = Terrain.activeTerrain.SampleHeight(new Vector3(randX, 0, randZ));
//Apply Offset if needed
yVal = yVal + yOffset;
//Generate the Prefab on the generated position
GameObject objInstance = (GameObject)Instantiate(prefab, new Vector3(randX, yVal, randZ), Quaternion.identity);
}
Related
I am creating a Space simultation in Unity using Newtons Law of gravitation and Centripetal force calculations to re-create the motion of the planets. Recently i have tried to implement realistic masses such as 3.285e+23 and i have converted them to managable numbers using a massScale 1e-24f. but since implementing these new masses and converting them, the smaller planet of the three has started deleting itself shortly after runtime without any error being thrown. I will also add that i dont have any sort of Destroy Line in my code either.
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlanetVelocity : MonoBehaviour
{
private Gravity _gravity;
public float v;
public bool debugBool;
public GameObject Sun;
private Rigidbody rb;
private Vector3 dif;
public static List<GameObject> AllPlanets;
private const float distScale = 1e-7f;
private const float massScale = 1e-24f;
private const float gravity = 6.674e-11f;
public float mass = 5.972e24f;
private Vector3 motion;
[Header("Orbital Data")]
public float velocity = 29.8f;
public float distance = 148900000f;
//TODO: Convert weight units to KG and use realistic weights for planetary bodies
/*Calculate velocity using v = sqrt[(G M )/r]*/
/*Get the Planet to look at the sun*/
/*Apply V to the transform.right of the planet to have perpendicular motion*/
void Start()
{
_gravity = FindObjectOfType<Gravity>();
rb = GetComponent<Rigidbody>();
rb.mass = mass * massScale;
}
void DebugClass()
{
if(debugBool)
{
Debug.Log($"Mass Scale {rb.mass}");
//Debug.DrawRay(gameObject.transform.position, motion, Color.red);
// Debug.Log($"Calculated Velocity = {v} Difference {dif} Motion {motion}");
}
}
void ObjectLook()
{
transform.LookAt(Sun.transform);
}
// Update is called once per frame
void Update()
{
DebugClass();
ObjectLook();
Vector3 sunPos = new Vector3(Sun.transform.position.x, Sun.transform.position.y, Sun.transform.position.z);
Vector3 planetPos = new Vector3(gameObject.transform.position.x, gameObject.transform.position.y,
gameObject.transform.position.z);
Vector3 dif = sunPos - planetPos;
float r = dif.sqrMagnitude;
v = Mathf.Sqrt(_gravity.f * rb.mass) / r;
//v = Mathf.Sqrt(_gravity.G * rb.mass / r ) ;
Vector3 motion = transform.up * v;
if (gameObject.transform.position.x > 0 && gameObject.transform.position.y > 0)
{
motion.x = Math.Abs(motion.x);
motion.y = -Math.Abs(motion.y);
}
if (gameObject.transform.position.x > 0 && gameObject.transform.position.y < 0)
{
motion.x = -Math.Abs(motion.x);
motion.y = -Math.Abs(motion.y);
}
rb.velocity = motion;
}
}
and here is my script controlling gravitational pull towards the sun, based off of what game objects are withing the sphere collider range of the sun.
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Gravity : MonoBehaviour
{
public float f; //force
private const float massScale = 1e-24f;
public float mass = 1.989e+30f;
public float G = 6.674e-11f; //Gravitational Constant
private Rigidbody m1;//Mass 1 The Sun ( this Game Object)
private Rigidbody m2;//Mass 2 Planet
private Vector3 r1;
public Vector3 FORCE;
public bool debugBool;
void Start()
{
Time.timeScale = 50f;
}
private void Awake()
{
m1 = GetComponent<Rigidbody>();
m1.mass = mass * massScale;
}
private void DebugClass()
{
if (debugBool)
{
Debug.Log(m2.velocity);
Debug.Log("TRIGGERED");
Debug.Log($"r1 {r1.normalized} FORCE {FORCE}");
}
}
private void Update()
{
DebugClass();
}
private void OnTriggerStay(Collider other)
{
//declares rigidbody locally
Rigidbody m2;
//assigns other objects rigidbody to m2
m2 = other.gameObject.GetComponent<Rigidbody>();
// get sun position
Vector3 Sun = new Vector3(gameObject.transform.position.x, gameObject.transform.position.y, gameObject.transform.position.z);
// planet
Vector3 otherPlanet = new Vector3(other.gameObject.transform.position.x, other.gameObject.transform.position.y, other.gameObject.transform.position.z);
//Difference between each object
Vector3 r1 = Sun - otherPlanet;
//Difference between each object squared
float r2 = r1.sqrMagnitude;
float distance = r1.magnitude;
///<summary>
/// Calculates Force by Mutliplying Gravitational
/// constant by the mass of each object divided
/// by the distance between each object squared
/// <summary>
f = G * m1.mass * m2.mass / r2;
//Assigns the value r1 normalized between 0 and 1
//multiplied by F independent of frames per second
Vector3 FORCE = r1.normalized * f * Time.deltaTime;
// Adds force to the other game objects rigidbody using the FORCE vector
// Using the Force force mode
m2.AddForce(FORCE);
}
}
I had issues with extremely small float numbers on my Rigidbody transform and thus unity was deactivating the game object before it overflowed, my solution was to multiply the physic calculations i used by a 'PhysicsMultiplier' and this solved my issues completely
I have written a small code, which is mooving vertices down on mouse click. The problem is in that mesh on change is getting black (only in some places).
Here's the code
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MinerScript : MonoBehaviour
{
public MeshFilter filter;
public MeshCollider col;
public GameObject Player;
public float mineSpeed = 200;
Mesh mesh;
Vector3[] vertices;
void Update()
{
filter = transform.parent.GetComponent<PlayerGravity>().mesh;
mesh = filter.GetComponent<MeshFilter>().sharedMesh;
vertices = mesh.vertices;
Ray ray = new Ray(transform.position, transform.forward * 2);
if (Physics.Raycast(ray))
{
if (Input.GetMouseButton(1))
{
int index = Mathf.RoundToInt(ClosestIndexToPoint(ray));
vertices[index] += (-Player.transform.eulerAngles) * Time.deltaTime * mineSpeed;
mesh.vertices = vertices;
mesh.RecalculateBounds();
mesh.RecalculateNormals();
Color[] colors = new Color[mesh.vertices.Length];
colors[index] = Color.red;
mesh.SetColors(colors);
//col.sharedMesh = mesh;
}
}
}
public float ClosestIndexToPoint(Ray ray)
{
if (Physics.Raycast(ray, out RaycastHit hit))
{
Mesh m = hit.transform.GetComponent<MeshFilter>().sharedMesh;
col = hit.transform.GetComponent<MeshCollider>();
int[] tri = new int[3] {
m.triangles[hit.triangleIndex * 3 + 0],
m.triangles[hit.triangleIndex * 3 + 1],
m.triangles[hit.triangleIndex * 3 + 2],
};
float closestDistance = Vector3.Distance(m.vertices[tri[0]], hit.point);
int closestVertexIndex = tri[0];
for (int i = 0; i < tri.Length; i++)
{
float dist = Vector3.Distance(m.vertices[tri[i]], hit.point);
if (dist < closestDistance)
{
closestDistance = dist;
closestVertexIndex = tri[i];
}
}
return closestVertexIndex;
}
else
return -1;
}
I don't think the problem is in color, as i've tried to apply the teture on it.
About code:
It throws a ray, and if it hits any mesh, it is calling func "ClosestIndexToPoint". It returns value of type float. This function gets the triangle a ray hits, and then finds the closest vertecs to it. Then it just mooves it down (or the rotation of our Player object, cause a have a non - flat mesh).
I am making a game which let you click on a ball and drag to draw a line renderer with two points and point it to a specific direction and when release I add force to the ball,
for now, I just want to know how can I limit the distance between those two points like give it a radius.
You can simply clamp it using a Mathf.Min.
Since you didn't provide any example code unfortunately here is some example code I made up with a simple plane with a MeshCollider, a child object with the LineRenderer and a camera set to Orthographic. You probably would have to adopt it somehow.
public class Example : MonoBehaviour
{
// adjust in the inspector
public float maxRadius = 2;
private Vector3 startPosition;
[SerializeField] private LineRenderer line;
[SerializeField] private Collider collider;
[SerializeField] private Camera camera;
private void Awake()
{
line.positionCount = 0;
line = GetComponentInChildren<LineRenderer>();
collider = GetComponent<Collider>();
camera = Camera.main;
}
// wherever you dragging starts
private void OnMouseDown()
{
line.positionCount = 2;
startPosition = collider.ClosestPoint(camera.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, transform.position.z)));
var positions = new[] { startPosition, startPosition };
line.SetPositions(positions);
}
// while dragging
private void OnMouseDrag()
{
var currentPosition = GetComponent<Collider>().ClosestPoint(camera.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, transform.position.z)));
// get vector between positions
var difference = currentPosition - startPosition;
// normalize to only get a direction with magnitude = 1
var direction = difference.normalized;
// here you "clamp" use the smaller of either
// the max radius or the magnitude of the difference vector
var distance = Mathf.Min(maxRadius, difference.magnitude);
// and finally apply the end position
var endPosition = startPosition + direction * distance;
line.SetPosition(1, endPosition);
}
}
This is how it could look like
I've written the following pseudo code, which may help you
float rang ;
Bool drag=true;
GameObject ball;
OnMouseDrag () {
if(drag) {
//Put your dragging code here
}
if (ball.transform.position>range)
Drag=false;
else Drage=true;
}
I was trying to use a for loop to instantiate cube as much fit on the screen. So i checked how wide the screen is and is checked how wide my cube was. Divided it by each other and it didnt work.
So what i think i did wrong is that Screen.width is in pixels and renderer.bounds.size.x is in world size.
How do i fix this? I didnt figure it out on the internet..
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class move : MonoBehaviour
{
public GameObject cubePrefab;
public static bool spawn;
float times;
int screenWidth;
float objectSize;
// Use this for initialization
void Start()
{
spawn = true;
screenWidth = Screen.width;
objectSize = GameObject.Find("vork").GetComponent<Renderer>().bounds.size.x;
times = screenWidth / objectSize;
}
// Update is called once per frame
void Update()
{
if(spawn == true)
{
for(int i=0; i < times;i++) {
Vector3 pos = new Vector3(i * 2, i *2, 0);
Instantiate(cubePrefab, pos, transform.rotation);
}
spawn = false;
}
}
}
2D game
You need to calculate pixel width. In order to calculate screen position from world position use Camera.WorldToScreenPoint. This is how a function could look like:
public static float GetPixelWidth(GameObject gO)
{
Bounds bounds = gO.GetComponent<Renderer>().bounds;
Vector3 boundsPos1 = Camera.main.WorldToScreenPoint(new Vector3(gO.transform.position.x + bounds.extents.x, gO.transform.position.y, gO.transform.position.y));
Vector3 boundsPos2 = Camera.main.WorldToScreenPoint(new Vector3(gO.transform.position.x - bounds.extents.x, gO.transform.position.y, gO.transform.position.y));
return Vector3.Distance(boundsPos1, boundsPos2);
}
And this is how to use the function in your code:
// Use this for initialization
void Start()
{
spawn = true;
screenWidth = Camera.main.pixelWidth;
objectSize = GetPixelWidth(GameObject.Find("vork"));
times = screenWidth / objectSize;
}
I have tested and it works with orthographic camera
When trying to print out (debug) my objects value, it gives me the position's value of where it's original position is (the startposition in world space). So my object is placed in -3.51 in y in world space before starting the game, therefore it only prints out -3,51 on every update despite the fact that in the inspector you can see the y position (in transform) changing when the object moves.
This is the code that does it:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// Icke procedurell
public class createLevels : MonoBehaviour {
public GameObject doodle;
public Transform doodleTransform;
public GameObject greenPlatform;
public float distMultiplier = 1f;
public float valueY;
public int ammountPlatforms = 10;
public float levelWidth = 3f;
public float minY = 0.2f;
public float maxY = 1.5f;
public Vector3 spawnPosition = new Vector3();
void Start()
{
for (int i = 0;i < ammountPlatforms; i++) // Instansierar en startsumma platformar.
{
spawnPosition.y += Random.Range(minY, maxY); // Ökning i y
spawnPosition.x = Random.Range(-levelWidth, levelWidth); // Ingen ökning i x..
Instantiate(greenPlatform, spawnPosition, Quaternion.identity);
}
}
void Update()
{
Debug.Log(doodle.GetComponent<Transform>().position.y);
/*
if (doodleTransform.position.y >= 10)
{
Debug.Log("I am over a certain distance.");
distMultiplier++;
Instantiate(greenPlatform, spawnPosition, Quaternion.identity);
}
*/
}
}
Also note that this is a script on another gameobject referencing my "wanted" gameobject's position.
To summarize the question, if you look in the update function, observe the "Debug.Log". It only prints out my startvalue, and i am trying to print out the objects position in y in realtime, being dynamic.