I am generating gameobjects (spheres) based on coordinates, which are stored in a .csv file. I have a Gameobject with a Single Sphere as primitive childobject. Based on the data the Object will clone this sphere 17 times and move them around. I can move the whole thing around like i want it to by accessing the parent object, but in editing mode the position of the root sphere makes it uneasy to use.
The following Code makes this possible.
public GameObject parentObj;
public TextAsset csvFile;
[SerializeField]
private float scaleDownFactor = 10;
private int index = 0;
//class Deck : MonoBehaviour
//{
[SerializeField]
private GameObject[] deck;
private GameObject[] instanciatedObjects;
private void Start()
{
Fill();
}
public void Fill()
{
instanciatedObjects = new GameObject[deck.Length];
for (int i = 0; i < deck.Length; i++)
{
instanciatedObjects[i] = Instantiate(deck[i]) as GameObject;
}
}
//}
// Update is called once per frame
void Update()
{
readCSV();
}
void readCSV()
{
string[] frames = csvFile.text.Split('\n');
int[] relevant = {
0
};
string[] coordinates = frames[index].Split(',');
for (int i = 0; i < 17; i++)
{
float x = float.Parse(coordinates[relevant[i] * 3]) / scaleDownFactor;
float y = float.Parse(coordinates[relevant[i] * 3+1]) / scaleDownFactor;
float z = float.Parse(coordinates[relevant[i] * 3+2]) / scaleDownFactor;
//objectTest.transform.Rotate(float.Parse(fields[1]), float.Parse(fields[2]), float.Parse(fields[3]));
//objectTest.transform.Translate(x, y, z);
//parentObj.transform.position = new Vector3(x, y, z);
instanciatedObjects[i].transform.position = new Vector3(parentObj.transform.position.x, parentObj.transform.position.y, parentObj.transform.position.z);
instanciatedObjects[i].transform.eulerAngles = new Vector3(parentObj.transform.eulerAngles.x, parentObj.transform.eulerAngles.y, parentObj.transform.eulerAngles.z);
//instanciatedObjects[i].transform.position = new Vector3(x, y, z);
instanciatedObjects[i].transform.Translate(x, y, z);
}
if (index < frames.Length - 1)
{
index++;
}
if (index >= frames.Length -1)
{
index = 0;
}
}
Here is a Screenshot:
So my question is:
How can I set the Position of this Sphere to one of the moving points, without changing the position of the cloned objects? Since all behave based on the BaseSphere?
Is it possible to make the BaseSphere not visible While the Objects are getting cloned or generated?
I am looking for a solution, that makes it easier to move the datagenerated Object around in Editor.
I would appreciate any kind of input.
Make all Spheres children of Empty Gameobject (for example. Sphere_root) and use this for moving Spheres.
Also check Scriptable Objects. It is simple and very quick method to manage data in Unity.
#Edit
public void Fill()
{
instanciatedObjects = new GameObject[deck.Length];
for (int i = 0; i < deck.Length; i++)
{
instanciatedObjects[i] = Instantiate(deck[i]) as GameObject;
instanciatedObjects[i].transform.parent = Baumer; // or Sphere Root or somehing else.
}
}
Related
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GenerateStairs : MonoBehaviour
{
public GameObject stairsPrefab;
public int delay = 3;
public int stairsNumber = 5;
public int stairsHeight = 0;
public Vector3 stairsPosition;
public Vector2 stairsSize;
// Use this for initialization
void Start ()
{
StartCoroutine(BuildStairs());
}
// Update is called once per frame
void Update ()
{
}
private IEnumerator BuildStairs()
{
for (float i = 0; i <= stairsSize.x; i++)
{
for (float k = 0; k <= stairsSize.y; k++)
{
stairsPosition = new Vector3(i, stairsHeight, k);
GameObject stairs = Instantiate(stairsPrefab, stairsPosition, Quaternion.identity);
stairs.transform.localScale = new Vector3(stairsSize.x, 1 , stairsSize.y);
stairsHeight += 1;
yield return new WaitForSeconds(delay);
}
}
}
private void CalculateNextStair()
{
}
}
I messed it up. For example I want to build 5 stairs but the loops are over the stairs size and not number of stairs.
Second it's creating 10 sets of stairs not 5 stairs:
Another problem is how can I make that each stair will be build slowly ? Now it's just Instantiate slowly with delay but how can I generate each stair with delay?
Screenshot of the script inspector:
My current code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GenerateStairs : MonoBehaviour
{
public GameObject stairsPrefab;
public float delay = 0.3f;
public int stairsNumber = 5;
public int stairsPositions = 0;
public int stairsStartPositionHeight = 0;
public float stairsScalingHaight = 1;
public Vector2 stairsPosition;
public Vector2 stairsSize;
// Use this for initialization
void Start()
{
StartCoroutine(BuildStairs());
}
// Update is called once per frame
void Update()
{
}
private IEnumerator BuildStairs()
{
for (float i = 0; i <= stairsNumber; i++)
{
// x=0f, y=z=stairsHeight
stairsPosition = new Vector3(0f, stairsPositions, stairsPositions);
GameObject stairs = Instantiate(
stairsPrefab,
stairsPosition,
Quaternion.identity);
stairs.transform.localScale = new Vector3(
stairsSize.x,
stairsScalingHaight,
stairsSize.y);
stairsStartPositionHeight += 1;
yield return new WaitForSeconds(delay);
}
}
private void CalculateNextStair()
{
}
}
There's no reason to loop over the size of the stairs at all; you want to loop over stairsNumber, which is yet unused in your code.
Also, you don't need to change the x component of your stairs' positions. Keep it at 0f (or whatever you need).
The y and z components of your stairs positions (relative to the starting point) should both be factors of stairHeight. In this particular case, you want them to be equal to stairHeight, so that you get "square" step shapes.
private IEnumerator BuildStairs()
{
for (int i = 0; i <= stairsNumber ; i++) {
// x=0f, y=z=stairsHeight
stairsPosition = new Vector3(0f, stairsHeight, stairsHeight);
GameObject stairs = Instantiate(
stairsPrefab,
stairsPosition,
Quaternion.identity);
stairs.transform.localScale = new Vector3(
stairsSize.x,
1f ,
stairsSize.y);
stairsHeight += 1f;
yield return new WaitForSeconds(delay);
}
}
If you change stairSize to be a Vector3, then you can just use stairSize directly as the localScale, and increment stairsHeight by stairsSize.y instead of just 1f.
If you want to offset the starting position of your stairs, you need to include an offset. I recommend keeping it separate from the height counter until you need to add them.
Also, if you want to have rectangular sized steps, keep a widthFactor to multiply by the height to find how far each step moves horizontally.
Combining these changes might look like this:
Vector3 stairSize;
float stepWidthFactor=1f;
Vector3 stairsStartPosition;
private IEnumerator BuildStairs() {
for (int i = 0; i <= stairsNumber ; i++) {
stairsPosition = new Vector3(
stairsStartPosition.x,
stairsStartPosition.y + stairsHeight,
stairsStartPosition.z + stairsHeight*stepWidthFactor);
GameObject stairs = Instantiate(
stairsPrefab,
stairsPosition,
Quaternion.identity);
stairsHeight += stairsSize.y;
stairs.transform.localScale = stairSize;
yield return new WaitForSeconds(delay);
}
}
The code below works Except for the Instancated objects moving on their own. I want my Instancated objects to move back and fourth between pointA and pointB at a speed 0.5f.
Note: Im not trying to use the commented code in Start() and Update() because this file is attached to the camera.
With current code: My objectList objects are moving as expected just not their instantiated objects. I would like the Instantiated objects to move like ping-pong with their off-set
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ObjectsSpawner : MonoBehaviour {
public GameObject[] objectList;
public GameObject[] objectSpawns;
int val;
public float speed = 0.5f;
Vector3 pointA;
Vector3 pointB;
// Use this for initialization
void Start () {
val = PlayerPrefs.GetInt("CannonPowerVal");
addToList();
for (int i = 1; i < objectSpawns.Length; i++){
pointA = new Vector3(-3.8f, objectSpawns[i].transform.localPosition.y, 0);
pointB = new Vector3(3.8f, objectSpawns[i].transform.localPosition.y, 0);
}
//pointA = new Vector3(-3.8f, transform.localPosition.y, 0);
//pointB = new Vector3(3.8f, transform.localPosition.y, 0);
}
// Update is called once per frame
void Update()
{
//PingPong between 0 and 1
float time = Mathf.PingPong(Time.time * speed, 1);
//transform.position = Vector3.Lerp(pointA, pointB, time);
for (int i = 1; i < objectSpawns.Length; i++)
{
objectSpawns[i].transform.position = Vector3.Lerp(pointA, pointB, time);
}
}
public void addToList(){
objectSpawns = new GameObject[val];
int max = objectList.Length;
int counter = 8; // set first object out of screen sight
// Adds Scene Objects from objectList to objectSpawns
// Size of SceneObjects determined by CannonPowerVal
for (int i = 0; i < PlayerPrefs.GetInt("CannonPowerVal"); i++){
objectSpawns.SetValue(objectList[Random.Range(0, max)], i);
// Random x spawn(-2.8f, 2.8f)
Instantiate(objectSpawns[i], new Vector2(transform.localPosition.x + Random.Range(-2.8f,2.8f), transform.localPosition.y + counter), Quaternion.identity);
counter = counter + 5;
}
}
}
after instantiating the object, you forgot to add the reference to the list of objectSpawns.
In addToList() method do:
GameObject object = Instantiate(objectSpawns[i],...);
objectSpawns.Add(object)
I have run into this issue where I have a GameObject, for example size 1x3 - and the gameobject is only recognized for being on a tile because it's placed there and given coordinates in the inspector (for example 5 x, 0 y, 6 z). The problem being that the code only detects that there is a GameObject on one tile, the middle tile of the grid code.
I need to make sure the other tiles the GameObject is covering is aware that there is a GameObject there, so I can set them all to for example that they're not walkable on - or that you can use them as cover by being next to them. But so far the code only registers 1 tile of the GameObject - therein lies the problem.
Thing is, I don't know where to start. I have some ideas, maybe do a loop to check tiles next to them if there is a gameobject but then the code won't know if something is on that tile and the loop would also be quite heavy on performance - unless I do that loop on Awake or Start or something similar.
Can anyone point me in the right direction? Is there some built-in thing in Unity that can detect if something is above the specific tile I created?
It's an own gridsystem for the record to support 3D, so I'm not using the built-in tilemaps system for 2D games that Unity offers these days.
Unity Version 2017.3
Down below is Gridbase.cs - this handles the world and spawns all nodes (tiles)
public class GridBase : MonoBehaviour
{
//Grid Scale
public int sizeX = 32;
//amount of floors/levels, Y is up
public int sizeY = 3;
public int sizeZ = 32;
public float scaleXZ = 1;
// character scale
public float scaleY = 2.3f;
public Node[,,] grid;
public List<YLevels> yLevels = new List<YLevels>();
public bool debugNode = true;
public Material debugMaterial;
GameObject debugNodeObj;
void Start()
{
InitPhase();
}
public void InitPhase()
{
if (debugNode)
debugNodeObj = WorldNode();
//debug check all values for the grid.
Check();
//Spawn Grid Function
CreateGrid();
GameManager.singleton.Init();
}
void Check()
{
if(sizeX == 0)
{
Debug.Log("Size x is 0, assigning min");
sizeX = 16;
}
if(sizeY == 0)
{
Debug.Log("Size y is 0, assigning min");
sizeY = 1;
}
if (sizeZ == 0)
{
Debug.Log("Size z is 0, assigning min");
sizeX = 1;
}
if (scaleXZ == 0)
{
Debug.Log("ScaleXZ is 0, assigning min");
scaleXZ = 1;
}
if (scaleY == 0)
{
Debug.Log("ScaleY is 0, assigning min");
scaleY = 2;
}
}
void CreateGrid()
{
grid = new Node[sizeX, sizeY, sizeZ];
for (int y = 0; y < sizeY; y++)
{
YLevels ylvl = new YLevels();
ylvl.nodeParent = new GameObject();
ylvl.nodeParent.name = "Level" + y.ToString();
ylvl.y = y;
yLevels.Add(ylvl);
//Creating Collision for all nodes)
CreateCollision(y);
for (int x = 0; x < sizeX; x++)
{
for (int z = 0; z < sizeZ; z++)
{
Node n = new Node();
n.x = x;
n.y = y;
n.z = z;
n.isWalkable = true;
if(debugNode)
{
Vector3 targetPosition = WorldCoordinatesFromNode(x, y, z);
GameObject go = Instantiate(debugNodeObj,
targetPosition,
Quaternion.identity ) as GameObject;
go.transform.parent = ylvl.nodeParent.transform;
}
grid[x, y, z] = n;
}
}
}
}
void CreateCollision(int y)
{
YLevels lvl = yLevels[y];
GameObject go = new GameObject();
BoxCollider box = go.AddComponent<BoxCollider>();
// Creates a box collider that has the whole size of the grid + a little bit more
box.size = new Vector3(sizeX * scaleXZ + (scaleXZ * 2),
0.2f,
sizeZ * scaleXZ + (scaleXZ * 2));
//Spawn box collider in center
box.transform.position = new Vector3((sizeX * scaleXZ) * .5f - (scaleXZ * .5f),
y * scaleY,
(sizeZ * scaleXZ) * 0.5f - (scaleXZ * .5f));
lvl.CollisionObj = go;
lvl.CollisionObj.name = "lvl " + y + " collision";
}
public Node GetNode(int x, int y, int z)
{
x = Mathf.Clamp(x, 0, sizeX - 1);
y = Mathf.Clamp(y, 0, sizeY - 1);
z = Mathf.Clamp(z, 0, sizeZ - 1);
return grid[x, y, z];
}
//get World Cordinates from any Node
public Vector3 WorldCoordinatesFromNode(int x, int y, int z)
{
Vector3 r = Vector3.zero;
r.x = x * scaleXZ;
r.y = y * scaleY;
r.z = z * scaleXZ;
return r;
}
GameObject WorldNode()
{
GameObject go = new GameObject();
GameObject quad = GameObject.CreatePrimitive(PrimitiveType.Quad);
Destroy(quad.GetComponent<Collider>());
quad.transform.parent = go.transform;
quad.transform.localPosition = Vector3.zero;
quad.transform.localEulerAngles = new Vector3(90, 0, 0);
quad.transform.localScale = Vector3.one * 0.95f;
quad.GetComponentInChildren<MeshRenderer>().material = debugMaterial;
return go;
}
public static GridBase singleton;
private void Awake()
{
singleton = this;
}
}
[System.Serializable]
public class YLevels
{
public int y;
public GameObject nodeParent;
public GameObject CollisionObj;
}
And this is the Node.cs file
public class Node
{
//Node's position in the grid
public int x;
public int y;
public int z;
//Node's costs for pathfinding purposes
public float hCost;
public float gCost;
public float fCost
{
get //the fCost is the gCost+hCost so we can get it directly this way
{
return gCost + hCost;
}
}
public Node parentNode;
public bool isWalkable = true;
//Reference to the world object so we can have the world position of the node among other things
public GameObject worldObject;
//Types of nodes we can have, we will use this later on a case by case examples
public NodeType nodeType;
public enum NodeType
{
ground,
air
}
}
this is the GridBase code pastebin.com/gazu9jPR that spawns the nodes (tiles) and this is the node script that triggers whenever gridbase references to Node class - pastebin.com/jk25n6ee
You can use trigger colliders at the place the gameobjects will be created on. You'll need a seperate collider for each grid cell. Make the gameobject with the collider the child of the grid cell (I don't exactly know your hierarchy, so I'll just keep guessing). Then, when the collider detects the gameobject, let the corresponding cell know that it is occupied. Hope this helps.
All the tiles need to have colliders. Also the object you place on the grid needs to have a collider(tick as trigger) which is big enough to overlap with tiles' colliders. Then you can use that to check how many tiles are covered by that object. The way you can do this is by using Collider.bounds which is of type Bounds. Bounds object has a method called Contains which you can pass in the tile's center point and see if it's covered by the object.
I'm trying to build a game for Android just like Minecraft using Unity. How can I save my progress?
I'm trying this code but I'm still clueless if I'm on the right track.
public class SavePref : MonoBehaviour {
GameObject[] objects;
float x;
float y;
float z;
// Use this for initialization
void Start () {
objects = GameObject.FindGameObjectsWithTag("ObjectSnap");
}
// Update is called once per frame
void Update () {
}
public void Load()
{
foreach (GameObject obj in objects)
{
obj.name = PlayerPrefs.GetString("Ojects");
x = PlayerPrefs.GetFloat("X");
y = PlayerPrefs.GetFloat("Y");
z = PlayerPrefs.GetFloat("Z");
}
}
public void Save()
{
objects = GameObject.FindObjectsOfType(typeof(GameObject)) as GameObject[];
Debug.Log(objects.Length);
foreach (GameObject obj in objects)
{
PlayerPrefs.SetString("Objects", obj.name);
Debug.Log(obj.name);
x = obj.transform.position.x;
PlayerPrefs.SetFloat("X", x);
y = obj.transform.position.y;
PlayerPrefs.SetFloat("Y", y);
z = obj.transform.position.z;
PlayerPrefs.SetFloat("Z", z);
Debug.Log(obj.transform.position.x);
Debug.Log(obj.transform.position.y);
Debug.Log(obj.transform.position.z);
}
}
}
The reason is you are overwriting the same values.
Every object in the scene will overwrite the same 'X' 'Y' 'Z' and 'Objects' variables of PlayerPerfs.
So, if you want to save just blocks positions,
each block in the scene has to have its own sceneID.
When you write the PlayerPerfs, use these IDs.
For example:
public GameObject[] inscene;
void SaveBlock(){
inscene = GameObject.FindGameObjectsWithType("ObjectSnap");
for(int i = 0; i < inscene.Length; i++){
PlayerPerfs.SetFloat(i.ToString() + "_x", x);
PlayerPerfs.SetFloat(i.ToString() + "_y", y);
PlayerPerfs.SetFloat(i.ToString() + "_z", z);
}
PlayerPerfs.SetInt("Count", inscene.Length);
}
void LoadBlocks(){
int count = PlayerPerfs.GetInt("Count");
inscene = new GameObject[count];
for(int i = 0; i < count; i++)
{
float x = PlayerPerfs.GetFloat(i.ToString() + "_x");
float y = PlayerPerfs.GetFloat(i.ToString() + "_y");
float z = PlayerPerfs.GetFloat(i.ToString() + "_z");
inscene[i] = GameObject.CreatePrimitive(PrimitiveType.Cube);
inscene[i].transform.position = new Vector3(x, y, z);
inscene[i].tag = "ObjectSnap";
}
}
This code will just save blocks positions, and will recreate the world with white cubes.
If you want to save the type of blocks, you should have all types of blocks as prefarbs, and instantiate prefabs, when Load().
P.S. Anyway, such realisation of Minecraft clone for Android is terrible.
Imagine, you have one little chunk (32*32*32) full of blocks, then the RAM will have to handle 32768 blocks in memory (which just kill the application).
So, you have to operate not blocks, but the sides of these blocks, to cull the sides, which aren't visible.
And you shouldn't save the scene information via PlayerPerfs. Use System.IO instead. As i know, PlayerPerfs, saves information in registry, which isn't good for such data.
I created a Quad. I assigned this script to the Quad that contains an array of Game Objects:
public class ShapeGrid : MonoBehaviour {
public GameObject[] shapes;
void Start(){
GameObject[,] shapeGrid = new GameObject[3,3];
StartCoroutine(UpdateGrid());
}
IEnumerator UpdateGrid(){
while (true) {
SetGrid ();
yield return new WaitForSeconds(2);
}
}
void SetGrid(){
int col = 3, row = 3;
for (int y = 0; y < row; y++) {
for (int x = 0; x < col; x++) {
int shapeId = (int)Random.Range (0, 4.9999f);
GameObject shape = Instantiate (shapes[shapeId]);
Vector3 pos = shapes [shapeId].transform.position;
pos.x = (float)x*3;
pos.y = (float)y*3;
shapes [shapeId].transform.position = pos;
}
}
}
}
I cloned those Game Objects so that they appear on a grid like this:
When the user clicks an object, it should disappear. What I did was place this script on every element in my array of Game Objects:
public class ShapeBehavior : MonoBehaviour {
void Update(){
if(Input.GetMouseButtonDown(0)){
Destroy(this.gameObject);
}
}
}
But what happens is when I click on an object to destroy it, every clone of that object will be destroyed. I want only the specific clone to be destroyed, not everything. How do I do that?
The problem is on your Input call, "Input.GetMouseButtonDown(0)" is true in every script when you click the button on mouse, no matter the position of mouse. Attach any kind of collider to gameObject and set it up and put the script with OnMouseDown() method, look here for more info : http://docs.unity3d.com/ScriptReference/MonoBehaviour.OnMouseDown.html
You can also use raycasting but that's more advanced way of solving this.
Also replace this.gameObject with just gameObject.