Populate Scriptable Objects Automatically - c#

A few of my Scriptable Objects(used for seeding my initial game data) contain large 2 dimensional arrays (like 10x10), the data of which i am generating with an external Python script. I do use the Odin inspector plugin as well, to serialize the 2d array for me and provide me with a nice representation of that array inside the Unity editor.
I am simply doing it like this :
[TableMatrix()]
public int[,] table = new int[10, 10];
and this is just an Odin SerializedScriptableObject class.
The problem is, I really want to avoid having to add the 10x10 elements by hand using the Unity editor and also I want my objects to have variable 2d array sizes, one could beb (10,10), another could be (5,5). Is there a way to populate my scriptable objects programmatically to achieve that ? (Or does the Odin inspector plugin support something like that if anyone knows ?)
Thanks !

Sorry for the late response #Spyros.
My approach will be similar to #D Manokhin approach, but instead of using jagged array I'll use a multidimensional array (cause you can build a custom editor script to visualize them, I'm pretty sure you can visualize jagged arrays on editor without plugins, I've never used Odin plugin).
So I delcare one class who will store the structs called TileData:
using UnityEngine;
using System.Collections;
[System.Serializable]
public class TileData
{
/*
* rows
[rowData][rowData][rowData]
[cell] ->value
->type
[cell] ->value
->type
[cell] ->value
->type
*/
[System.Serializable]
public struct cell
{
public float value;
public string type;
}
[System.Serializable]
public struct rowData
{
public cell[] row;
}
public rowData[] rows;
}
I used value and type as an "examples", it's absolutly up to you, you can also store Vector3 if you want!
Then, how to call and use this kind of structure? Let's see:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Matrix : MonoBehaviour {
//declare the structure that will store the data
public TileData tileData = new TileData();
//those are public so you could make the matrix with the size you want..
//..ideally dynamically call it from your python file requisits
public int horizontalMatrixSize = 10;
public int verticalMatrixSize = 10;
// Use this for initialization
void Start()
{
//Create the matrix structure and fill it
tileData.rows = new TileData.rowData[horizontalMatrixSize];
for (int i = 0; i < tileData.rows.Length; i++)
{
tileData.rows[i].row = new TileData.cell[verticalMatrixSize];
for (int u = 0; u < tileData.rows[i].row.Length; u++)
{
tileData.rows[i].row[u].value = GetValuesFromPythonFileOrWhatever();
tileData.rows[i].row[u].type = GetValuesFromPythonFileOrWhatever();
}
}
}
}
But if you really like the jagged structure, you can still use it (but remember that will not be represented on editor) like this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Matrix : MonoBehaviour {
//declare the structure that will store the data
[SerializeField] private float[,] grayscaleBidimensional = null;
//those are public so you could make the matrix with the size you want..
//..ideally dynamically call it from your python file requisits
public int horizontalMatrixSize = 10;
public int verticalMatrixSize = 10;
// Use this for initialization
void Start()
{
//Create the matrix structure and fill it
tileData.rows = new TileData.rowData[horizontalMatrixSize];
grayscaleBidimensional = new float[horizontalMatrixSize, verticalMatrixSize];
for (int x = 0; x < horizontalMatrixSize; x++)
{
for (int y = 0; y < verticalMatrixSize; y++)
{
grayscaleBidimensional[x, y] = GetValuesFromPythonFileOrWhatever();
}
}
}
}
Remember that you can make the values of the objects as you want, so they don't need to have an static size!

To change an individual value, you can do table[x,y] = (your value). Lets say you want to fill every value with the number one, you could do:
for (int y = 0; y < 10; y++)
{
for (int x = 0; x < 10; x++)
{
table[x, y] = 1;
}
}
Hope that answers your question and sorry if the code is wrong - I don't have access to Unity at the moment, so you may need to fiddle around with it.

You should serialize the arrays out to files in python, and then use a Scripted Importer to import them into Unity, construct the arrays in the ScriptableObject, and fill in their values.
https://docs.unity3d.com/Manual/ScriptedImporters.html

Related

Read coordinates from txt file in Unity3D

i've just started to learn unity and i'm having some troubles to go ahead with my exercises.
I need to move 2 cars using coordinates stored in a .txt file called "positions.txt".
The cars have to move at the same time.
Inside the "cartesian" list there are numbers which have to be splitted in groups of 4 (time, x, y, z).
I'd like to create 2 arrays for each car: a time array and a Vector3 array.
First of all, i need to read the file and store the numbers in the arrays. I found several options and i tried this one without success: https://allison-liem.medium.com/unity-reading-external-json-files-878ed0978977
Which approach should i follow?
Thank you.
File "positions.txt"
[
{
"id":"car_1",
"position":{
"cartesian":[
1,0,0,0,
2,0,0,2,
3,1,0,3,
4,1,0,6,
5,2,1,9,
6,2,0,11,
7,3,1,13,
8,3,0,15,
9,3,1,18,
10,4,1,20]
}
},
{
"id":"car_2",
"position":{
"cartesian":[
1,0,0,0,
2,0,0,2,
3,1,0,3,
4,1,0,6,
5,2,1,9,
6,2,0,11,
7,3,1,13,
8,3,0,15,
9,3,1,18,
10,4,1,20]
}
}
]
So first of all you have a JSON so see Serialize and Deserialize Json and Json Array in Unity
I would recommend the Newtonsoft Json.NET package and do e.g.
[Serializable]
public class Position
{
// you can also go for array if you liked
// in JSON it is the same
public List<int> cartesian;
}
[Serializable]
public class Car
{
public string id;
public Position position;
}
and then
var json = File.RealAllText(FILEPATH);
// again could also go for array here
List<Car> cars = JsonConvert.DeserializeObject<List<Car>>(json);
However, the cartesian arrays are a bit "stupid" - have in mind that once this is handled as a list/array there are no line breaks and no good meaning for your redundant indices (1, 2, 3, 4, ...) at the beginning of each set.
I'd like to create 2 arrays for each car: a time array and a Vector3 array.
his wouldn't make sense in my eyes - if something that information clearly belongs coupled together so splitting it into individual arrays would be bad.
Either way you will have to manually extract your values when iterating over the list/array
Go in sets of 4 items
Ignore the first - it seems to be a redundant index
take the last three items and fill them into x,y,z of a vector
like e.g.
[Serializable]
public class Position
{
// you can also go for array if you liked
// in JSON it is the same
public List<int> cartesian;
public List<Vector3> GetPositions()
{
var count = cartesian.Count / 4;
var result = new List<Vector3>(count);
for(var resultIndex = 0; resultIndex < count; resultIndex++)
{
var cartesianIndex = resultIndex * 4;
var position = new Vector3(cartesian[cartesianIndex + 1], cartesian[cartesianIndex + 2], cartesian[cartesianIndex + 3]);
result.Add(position);
}
}
}
or if you actually need also that first value I would use a custom type like e.g.
public readonly struct TimeFrame
{
public int Time { get; }
public Vector3 Position { get; }
public TimeFrame(int time, Vector position)
{
Time = time;
Position = position;
}
}
and then adjust accordingly
[Serializable]
public class Position
{
// you can also go for array if you liked
// in JSON it is the same
public List<int> cartesian;
public List<TimeFrame> GetPositions()
{
var count = cartesian.Count / 4;
var result = new List<TimeFrame>(count);
for(var resultIndex = 0; resultIndex < count; resultIndex++)
{
var cartesianIndex = resultIndex * 4;
var time = cartesian[cartesianIndex];
var position = new Vector3(cartesian[cartesianIndex + 1], cartesian[cartesianIndex + 2], cartesian[cartesianIndex + 3]);
result.Add(new TimeFrame(time, position));
}
}
}
I saw that the medium article you're referring to uses Unity's built in JSON parsing library which does not support array parsing, as pointed out by this answer here https://stackoverflow.com/a/36244111/13489126.
That is the reason why your approach was not working.
I would recommend you to use Newtonsoft Json Parser instead of Unity's.

Storing 2D Array with Protobuf (C#)

I have a large multi-dimensional array that needs to be stored with protobuf. The array could have up to 5120*5120 = 26,214,400 items in it. Protobuf does not support storing multi-dimensional arrays, unfortunately.
As a test, I wrote two functions and an extra class. The class stores and x,y which points to the location inside of the array (array[x, y]). The class has a "value" that is the data from the array[x,y]. I use a List to store this data.
When I generate a fairly small array (1024*1024) I get an output file that is over 169MB. From my testing, it loads and generates the file extremely fast so there's no issue there. However, the file size is huge - I definitely need to cut down on size.
Is this a normal file size, or do I to rethink my entire process? Should I compress the data before saving it (zipping the file takes it from 169MB to 6MB)? If so, what's the fastest/easiest way to zip a file in C#?
This is pseudo code that is based on my real code.
[ProtoContract]
public class Example
{
[ProtoIgnore]
public string[,] MyArray { get; set; }
[ProtoMember(0)]
private List<MultiArray> Storage { get; set; }
public void MoveToList()
{
for (int x = 0; x < MyArray.GetLength(0); x++)
{
for (int y = 0; y < MyArray.GetLength(1); y++)
{
Storage.Add(new MultiArray
{
_x = x,
_y = y,
value = MyArray[x, y]
});
}
}
}
public void MoveToArray()
{
MyArray = new string[1024, 1024];
for (int i = 0; i < Storage.Count; i++)
{
MyArray[Storage[i].X, Storage[i].Y] = Storage[i]._value;
}
}
}
[ProtoContract]
public class MultiArray
{
[ProtoMember(0)]
public int _y { get; set; }
[ProtoMember(1)]
public int _x { get; set; }
[ProtoMember(2)]
public string _value { get; set; }
}
Notes: The value must be the correct x/y of the array.
I appreciate any suggestions.
I don't know about the storage but this is probably not the right way to do it.
The way you are doing it, you are creating a MultiArray object for every cell of your array.
A simplier and more efficient solution would be to do that:
String[] Storage = new String[1024*1024];
int width = 1024
int height = 1024;
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
Storage[x*width+y]=MyArray[x,y];
}
}
Ultimately, the protobuf format doesn't have a concept of arrays of higher dimension than one.
At the library level since you're using protobuf-net we could have the library do some magic here, essentially treating it as;
message Array_T {
repeated int32 dimensions;
repeated T items; // packed when possible
}
(noting that .proto doesn't actually support generics, but that doesn't really matter at the library level)
However, this would be a little awkward from a x-plat perspective.
But to test whether this would help, you could linearize your 2D array, and see what space it takes.
In your case, I suspect the real problem (re the size) is the quantity of strings. Protobuf writes string contents every time, without any attempt at lookup tables. It may also be worth checking what the sunlm total of string lengths (in UTF-8 bytes) is for your array contents.

Unity: How to represent grid map data in ECS

I have been trying to work with Unity's pure ECS approach to make a basic nxn grid of tiles.
Before using ECS I would just use a 2d array Tiles[,] grid where I can simply index (x,y) to find the tile I want.
Now moving into ECS, I want to create an entity for each tile using an IComponentData struct like so:
public struct Tile : IComponentData
{
public int xIndex;
public int yIndex;
public int isValid;
}
Somewhere during the start of my game I create the tile entities
for (int i = 0; i < tilesInMap; i++)
{
Entity tileEntity = entityManager.CreateEntity(tileArchetype);
int xIndex = i % terrainSize;
int yIndex = i / terrainSize;
entityManager.SetComponentData(tileEntity, new Position { Value = new float3(xIndex , 0, yIndex) });
Tile newTile;
newTile.xIndex = xIndex;
newTIle.yIndex = yIndex;
newTile.isValid = 1;
entityManager.SetComponentData(tileEntity, newTile);
}
Now somewhere else in my code I have a ComponentSystem that pulls in the tiles using a group struct
public struct TileGroup
{
public readonly int Length;
public EntityArray entity;
public ComponentDataArray<Tile> tile;
}
[Inject] TileGroup m_tileGroup;
As far as I'm aware, as long as I don't change the archetype of any of those entities in the TileGroup (for instance by calling AddComponent or RemoveComponent), the ordering of that injected array will be preserved (I'm not even sure of that!)
So say in my OnUpdate method I want to "get the tile at grid coordinate (23, 43)". I can calculate this (assuming a 1d array of tiles with the order preserved) by
int arrayIndex = yIndex * tilesPerMapWidth + xIndex;
But this only works as long as the injected array order of tiles is preserved which I doubt it will be eventually.
So a few questions:
1. Should I ever rely on the order of an injected array for behaviour logic?
2. Are there any better methods in achieving what I want using ECS?
From the Unity forums, "Order of Entities":
ComponentDataArray<> / ComponentGroup makes zero gurantees on the actual ordering of your entities except for that its deterministic. But generally the indices are not stable. (E.g. Adding a component to an entity, will change the index in ComponentDataArray of that entity and likely of another one) The only stable id is the "Entity" ID.
To answer your questions:
So a few questions: 1. Should I ever rely on the order of an injected array for behaviour logic?
Based on "...makes zero guarantees on the actual ordering of your entities..." I would say no.
Are there any better methods in achieving what I want using ECS?
You don't need arrayIndex to access entities in your tile group. You can iterate through the entities and access x and y for each to perform the required behavior. Depending on your actual functionality you may want to use the job system as well.
for (int i = 0; i < m_tileGroup.Length; i++) {
int x = m_tileGroup.tile[i].xIndex;
int y = m_tileGroup.tile[i].yIndex;
// do something with x and y
}

How to change the values in two dimension array

I am stuck with this two dimension array with Unity framework coding with c#. I am trying to create a Checker game with an Intelligent agent. So I trying to get all the pieces and passing to the Alpha Beta algorithm and get the maximum amount of Opponent side. But in order to do that I have to check each piece on my board. I am using 8by8 board on my game. So I used
Piece[,] pieces = new Pieces[8,8];
code in order to store the pieces. Now I want to change one piece and retrieve the pieces.
I tried in several ways and experimented, But I could not found any to take this array and change one generic object and retrieve the list with the changes.
Please help me to solve this. Thanks in advance.
I written down here about what I experimented. Any suggestions about this matter or did I do something wrong?....
internal static void TryMoveInImaginaryWay(Piece[,] piece)
{
int x = 0;
int y =2;
int x1 = 1;
int y1 = 3;
Moves move = new Moves();
move.X = x;
move.Y = y;
move.X1 = x1;
move.Y1 = y1;
// Copping the pieces state
Piece p = piece[x, y];
//I am creating a list with those pieces
List<Piece> temppiecelist = new List<Piece>();
foreach (Piece pi in piece)
{
if (pi == p)
{
piece[x1, y1] = p;
temppiecelist.Add(null);
}
else
{
temppiecelist.Add(pi);
}
}
Piece[,] piek = temppiecelist; // can't convert it says Cannot Implicitly convert type "System.Collection.Generic.List<Piece>' to Piece[*,*]'"
// I know i can't convert like this, but any ideas.
//Piece[,] piek = new Piece[8, 8];
I am asking about , Is there any way to change above Piece[,] array list values. And I want the same format as output.
It looks like what you are trying to do is make a copy of the board and then make a move on that board.
Why on you would get a List involved I cannot figure out; what led you to believe that a List was the right solution? I am interested to know; by learning why people come to bad conclusions about program writing, I can help them write better programs.
To make a mutated copy of the board, just copy the board and mutate the copy:
internal static void TryMoveInImaginaryWay(Piece[,] original)
{
int x0 = 0;
int y0 = 2;
int x1 = 1;
int y1 = 3;
Piece[,] copy = (Piece[,])original.Clone();
copy[x1,y1] = copy[x0,y0];
copy[x0,y0] = null;
And you're done.

C# - can you name a matrix with the contents of a string

Basically I have x amount of matrices I need to establish of y by y size. I was hoping to name the matrices: matrixnumber1 matrixnumber2..matrixnumbern
I cannot use an array as its matrices I have to form.
Is it possible to use a string to name a string (or a matrix in this case)?
Thank you in advance for any help on this!
for (int i = 1; i <= numberofmatricesrequired; i++)
{
string number = Convert.ToString(i);
Matrix (matrixnumber+number) = new Matrix(matrixsize, matrixsize);
}
You can achieve a similar effect by creating an array of Matrices and storing each Matrix in there.
For example:
Matrix[] matrices = new Matrix[numberofmatricesrequired];
for (int i = 0; i < numberofmatricesrequired; i++)
{
matrices[i] = new Matrix(matrixsize, matrixsize);
}
This will store a bunch of uniques matrices in the array.
If you really want to you could create a Dictionary<String, Matrix> to hold the matrices you create. The string is the name - created however you like.
You can then retrieve the matrix by using dict["matrix1"] (or whatever you've called it).
However an array if you have a predetermined number would be far simpler and you can refer to which ever you want via it's index:
Matrix theOne = matrix[index];
If you have a variable number a List<Matrix> would be simpler and if you always added to the end you could still refer to individual ones by its index.
I'm curious why you cannot use an array or a List, as it seems like either of those are exactly what you need.
Matrix[] matrices = new Matrix[numberofmatricesrequired];
for (int i = 0; i < matrices.Length; i++)
{
matrices[i] = new Matrix(matrixsize, matrixsize);
}
If you really want to use a string, then you could use a Dictionary<string, Matrix>, but given your naming scheme it seems like an index-based mechanism (ie. array or List) is better suited. Nonetheless, in the spirit of being comprehensive...
Dictionary<string, Matrix> matrices = new Dictionary<string, Matrix>();
for (int i = 1; i <= numberofmatricesrequired; i++)
{
matrices.Add("Matrix" + i.ToString(), new Matrix(matrixsize, matrixsize));
}

Categories