How can I save 3-dimension array into file and load later - arrays

I'm trying to save the 3D array witch has position data of the blocks with Unity and I can't find out how to save it.
public class Block
{
public Vector3 position;
public short blockType;
public byte facing;
public Block(Vector3 pos, short t, byte f)
{
position = pos;
blockType = t;
facing = f;
}
}
This is the block class which I stored the information about block.
public Block[,,] WorldBlock = new Block[100, 10, 100];
This is the array I want to save and it has 100000 blocks in it.

There are many ways how to approach this.
One way would e.g. be Newtonsoft JSON (comes as a package via the PackageManager and even pre-installed in latest Unity versions)
using Newtonsoft.Json;
....
public Block[,,] WorldBlock = new Block[100, 10, 100];
private string filePath => Path.Combine(Application.persistentDataPath, "example.json");
private void Save()
{
var json = JsonConvert.SerializeObject(WorldBlock);
File.WriteAllText(filePath, json);
}
private void Load()
{
if (File.Exists(filePath))
{
var json = File.ReadAllText(filePath);
WorldBlock = JsonConvert.DeserializeObject<Block[,,]>(json);
}
var block = WorldBlock[1, 2, 3];
Debug.Log($"{block.position} - {block.blockType} - {block.facing}");
}
Or - since JSON wastes a lot of character space for your use case - you could also implement you own binary serialization e.g. usingBinaryReader and BinaryWriter
in something like e.g.
[Serializable]
public class Block
{
public Vector3 position;
public short blockType;
public byte facing;
public Block(Vector3 pos, short t, byte f)
{
position = pos;
blockType = t;
facing = f;
}
public void Serialize(BinaryWriter writer)
{
writer.Write(position.x);
writer.Write(position.y);
writer.Write(position.z);
writer.Write(blockType);
writer.Write(facing);
}
public void Deserialize(BinaryReader reader)
{
position. x = reader.ReadSingle();
position. y = reader.ReadSingle();
position. z = reader.ReadSingle();
blockType = reader.ReadInt16();
facing = reader.ReadByte();
}
}
and then do
private void Save()
{
using (var stream = File.Open(filePath, FileMode.OpenOrCreate, FileAccess.Write))
{
using (var writer = new BinaryWriter(stream))
{
// first store the size of each dimension
for (var i = 0; i < WorldBlock.Rank; i++)
{
writer.Write(WorldBlock.GetLength(i));
}
// then serialize all blocks
for (var i = 0; i < WorldBlock.GetLength(0); i++)
{
for (var j = 0; j < WorldBlock.GetLength(1); j++)
{
for (var k = 0; k < WorldBlock.GetLength(2); k++)
{
var block = WorldBlock[i, j, k];
block.Serialize(writer);
}
}
}
}
}
}
private void Load()
{
if (File.Exists(filePath))
{
using (var stream = File.Open(filePath, FileMode.Open, FileAccess.Read))
{
using (var reader = new BinaryReader(stream))
{
// first get th size of each dimension
var x = reader.ReadInt32();
var y = reader.ReadInt32();
var z = reader.ReadInt32();
WorldBlock = new Block[x, y, z];
// then deserialize all blocks
for (var i = 0; i < WorldBlock.GetLength(0); i++)
{
for (var j = 0; j < WorldBlock.GetLength(1); j++)
{
for (var k = 0; k < WorldBlock.GetLength(2); k++)
{
var block = new Block();
block.Deserialize(reader);
WorldBlock[i, j, k] = block;
}
}
}
}
}
}
var exampleBlock = WorldBlock[1, 2, 3];
Debug.Log($"{exampleBlock.position} - {exampleBlock.blockType} - {exampleBlock.facing}");
}

Related

My unity save system doesn't save integer array

I don't get any errors but my script doesn't load or possibly save my array...
here's the first script
[System.Serializable]
public class PlayerData
{
public float health;
public float thirst;
public float hunger;
public float oxygen;
public float[] position;
public int[] inventoryIDs;
public PlayerData (Health healthO, SaveLoad saveload)
{
//Save int items
health = healthO.health;
thirst = healthO.thirst;
hunger = healthO.hunger;
oxygen = healthO.oxygen;
//set and save location array
position = new float[3];
position[0] = healthO.transform.position.x;
position[1] = healthO.transform.position.y;
position[2] = healthO.transform.position.z;
//set and save inventory IDs
inventoryIDs = new int[50];
for(int i = 0; i < 50; i++)
{
inventoryIDs[i] = saveload.IDs[i];
}
}
}
here's the next
public class SaveLoad : MonoBehaviour
{
public GameObject player;
public int[] IDs;
public GameObject[] objects;
Inventory inventory;
Health health;
void Start()
{
IDs = new int[50];
objects = new GameObject[50];
inventory = player.GetComponent<Inventory>();
health = player.GetComponent<Health>();
}
void Update()
{
//Add IDs
for (int i = 0; i < 50; i++)
{
IDs[i] = inventory.slot[i].GetComponent<Slot>().ID;
}
//debug save load test
if (Input.GetKeyDown(KeyCode.Z))
{
SaveP();
}
if (Input.GetKeyDown(KeyCode.X))
{
LoadP();
}
}
public void SaveP()
{
SaveSystem.SavePlayer(health, this);
}
public void LoadP()
{
PlayerData data = SaveSystem.LoadPlayer();
//load stats
health.thirst = data.thirst;
health.hunger = data.hunger;
health.health = data.health;
health.oxygen = data.oxygen;
//Load position
Vector3 position;
position.x = data.position[0];
position.y = data.position[1];
position.z = data.position[2];
player.transform.position = position;
//load IDs
for (int i = 0; i < 50; i++)
{
IDs[i] = data.inventoryIDs[i];
}
//Load Items
for (int i = 0; i < 50; i++)
{
if(objects[IDs[i]] != null)
{
GameObject itemObject = (GameObject)Instantiate(objects[IDs[i]], new Vector3(0, 0, 0), Quaternion.identity);
Item item = itemObject.GetComponent<Item>();
inventory.AddItem(itemObject, item.ID, item.type, item.name, item.description, item.icon);
} else
{
return;
}
}
}
}
Here's the last script
public static class SaveSystem
{
public static string fileName = "FileSave.bin";
public static void SavePlayer(Health health, SaveLoad SL)
{
//Create formatter
BinaryFormatter bf = new BinaryFormatter();
// Create file stream
FileStream file = File.Create(GetFullPath());
//Save data
PlayerData data = new PlayerData(health, SL);
bf.Serialize(file, data);
//Close stream
file.Close();
}
public static PlayerData LoadPlayer()
{
if (SaveExists())
{
try
{
//Create formatter
BinaryFormatter bf = new BinaryFormatter();
//Create file stream
FileStream file = File.Open(GetFullPath(), FileMode.Open);
//Load data
PlayerData pd = (PlayerData)bf.Deserialize(file);
//close stream
file.Close();
//return data
return pd;
}
catch (SerializationException)
{
Debug.Log("Failed to load file at: " + GetFullPath());
}
}
return null;
}
private static bool SaveExists()
{
return File.Exists(GetFullPath());
}
private static string GetFullPath()
{
return Application.persistentDataPath + "/" + fileName;
}
}
there are all connected with the save load script loading and saving the variables into the player and items to the inventory sots. the inventory IDs array isn't saving or loading

unity array having 50 gameobjects but displays only 10 object

i have build a level menu with levels, i also created and empty array (GameObject[ ] lvlBut; )to store the level icons instantiated, but only 10 level icons are displayed on the screen where as i have 50 levels. for some reason its only taking 10 levels and i don’t t know where i have gone wrong. any suggestions?
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.UI;
using TMPro;
public class LevelSelector : MonoBehaviour{
public GameObject levelHolder;
public GameObject levelIcon;
public GameObject thisCanvas;
public int numberOfLevels = 50;
public Vector2 iconSpacing;
private Rect panelDimensions;
private Rect iconDimensions;
private int amountPerPage;
private int currentLevelCount;
int levelsUnlocked;
GameObject[] lvlBut;
void Start()
{
panelDimensions = levelHolder.GetComponent<RectTransform>().rect;
iconDimensions = levelIcon.GetComponent<RectTransform>().rect;
int maxInARow = Mathf.FloorToInt((panelDimensions.width + iconSpacing.x) / (iconDimensions.width + iconSpacing.x));
int maxInACol = Mathf.FloorToInt((panelDimensions.height + iconSpacing.y) / (iconDimensions.height + iconSpacing.y));
amountPerPage = maxInARow * maxInACol;
int totalPages = Mathf.CeilToInt((float)numberOfLevels / amountPerPage);
LoadPanels(totalPages);
}
void LoadPanels(int numberOfPanels)
{
GameObject panelClone = Instantiate(levelHolder) as GameObject;
PageSwiper swiper = levelHolder.AddComponent<PageSwiper>();
swiper.totalPages = numberOfPanels;
for (int i = 1; i <= numberOfPanels; i++)
{
GameObject panel = Instantiate(panelClone) as GameObject;
panel.transform.SetParent(thisCanvas.transform, false);
panel.transform.SetParent(levelHolder.transform);
panel.name = "Page-" + i;
panel.GetComponent<RectTransform>().localPosition = new Vector2(panelDimensions.width * (i - 1), 0);
SetUpGrid(panel);
int numberOfIcons = i == numberOfPanels ? numberOfLevels - currentLevelCount : amountPerPage;
LoadIcons(numberOfIcons, panel);
}
Destroy(panelClone);
}
void SetUpGrid(GameObject panel)
{
GridLayoutGroup grid = panel.AddComponent<GridLayoutGroup>();
grid.cellSize = new Vector2(iconDimensions.width, iconDimensions.height);
grid.childAlignment = TextAnchor.MiddleCenter;
grid.spacing = iconSpacing;
}
void LoadIcons(int numberOfIcons, GameObject parentObject)
{
for (int i = 1; i <= numberOfIcons; i++)
{
currentLevelCount++;
GameObject icon = Instantiate(levelIcon) as GameObject;
icon.transform.SetParent(thisCanvas.transform, false);
icon.transform.SetParent(parentObject.transform);
icon.name = "Level " + i;
icon.GetComponentInChildren<TextMeshProUGUI>().SetText(currentLevelCount.ToString());
}
lvlBut = GameObject.FindGameObjectsWithTag("LevelButton");
levelsUnlocked = PlayerPrefs.GetInt("levelsUnlocked", 1);
for (int i = 0; i < lvlBut.Length; i++)
{
lvlBut[i].GetComponentInChildren<Button>().interactable = false;
}
for (int i = 0; i < levelsUnlocked; i++)
{
lvlBut[i].GetComponentInChildren<Button>().interactable = true;
}
}
}

How can we copy an array with multiple dimension to the Kernel with AleaGpu?

How can we copy an array with multiple dimension to the Kernel in AleaGPU ?
How can we develop with a multiple dimension array in a Kernel ?
Malloc don't seem to accept it ?
double[,] inputs;
double[,] dInputs1 = Worker.Malloc(inputs); // I get an error here
var dOutputs1 = Worker.Malloc<double>(inputs1.Length)
Worker.Launch(SquareKernel, lp, dOutputs1.Ptr, dInputs1.Ptr, inputs.Length); //dInputs1.Ptr Make an error
[AOTCompile]
static void SquareKernel(deviceptr<double> outputs, deviceptr<double[,]> inputs, int n)
{
var start = blockIdx.x * blockDim.x + threadIdx.x;
var stride = gridDim.x * blockDim.x;
for (var i = start; i < n; i += stride)
{
outputs[i] = inputs[i,0] * inputs[i,0];
}
}
Alea GPU version until 2.2 (newest for now) doesn't support malloc array2d yet, so you have to flatten the index by row and col by yourself in kernel. For host side, you can make some extension method, to use some CUDA Driver API P/Invoke (These P/Invoke function is available from Alea.CUDA.dll) to trasfer a pinned .NET array to or from device.
So here is a quick workround I wrote:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using Alea.CUDA;
using Alea.CUDA.IL;
using NUnit.Framework;
namespace ConsoleApplication1
{
static class Extension
{
public static DeviceMemory<T> Malloc<T>(this Worker worker, T[,] array2D)
{
var rows = array2D.GetLength(0);
var cols = array2D.GetLength(1);
var dmem = worker.Malloc<T>(rows*cols);
var handle = GCHandle.Alloc(array2D, GCHandleType.Pinned);
try
{
var hostPtr = handle.AddrOfPinnedObject();
var devicePtr = dmem.Handle;
// we now pinned .NET array, and need to copy them with CUDA Driver API
// to do so we need use worker.Eval to make sure the worker's context is
// pushed onto current thread.
worker.EvalAction(() =>
{
CUDAInterop.cuSafeCall(CUDAInterop.cuMemcpyHtoD(devicePtr, hostPtr,
new IntPtr(Intrinsic.__sizeof<T>()*rows*cols)));
});
}
finally
{
handle.Free();
}
return dmem;
}
public static DeviceMemory<T> Malloc<T>(this Worker worker, int rows, int cols)
{
return worker.Malloc<T>(rows*cols);
}
public static void Gather<T>(this DeviceMemory<T> dmem, T[,] array2D)
{
var rows = array2D.GetLength(0);
var cols = array2D.GetLength(1);
var handle = GCHandle.Alloc(array2D, GCHandleType.Pinned);
try
{
var hostPtr = handle.AddrOfPinnedObject();
var devicePtr = dmem.Handle;
// we now pinned .NET array, and need to copy them with CUDA Driver API
// to do so we need use worker.Eval to make sure the worker's context is
// pushed onto current thread.
dmem.Worker.EvalAction(() =>
{
CUDAInterop.cuSafeCall(CUDAInterop.cuMemcpyDtoH(hostPtr, devicePtr,
new IntPtr(Intrinsic.__sizeof<T>() * rows * cols)));
});
}
finally
{
handle.Free();
}
}
}
class Program
{
static int FlattenIndex(int row, int col, int cols)
{
return row*cols + col;
}
[AOTCompile]
static void Kernel(deviceptr<double> outputs, deviceptr<double> inputs, int rows, int cols)
{
// for simplicity, I do all things in one thread.
for (var row = 0; row < rows; row++)
{
for (var col = 0; col < cols; col++)
{
outputs[FlattenIndex(row, col, cols)] = inputs[FlattenIndex(row, col, cols)];
}
}
}
[Test]
public static void Test()
{
var worker = Worker.Default;
// make it small, for we only do it in one GPU thread.
const int rows = 10;
const int cols = 5;
var rng = new Random();
var inputs = new double[rows, cols];
for (var row = 0; row < rows; ++row)
{
for (var col = 0; col < cols; ++col)
{
inputs[row, col] = rng.Next(1, 100);
}
}
var dInputs = worker.Malloc(inputs);
var dOutputs = worker.Malloc<double>(rows, cols);
var lp = new LaunchParam(1, 1);
worker.Launch(Kernel, lp, dOutputs.Ptr, dInputs.Ptr, rows, cols);
var outputs = new double[rows, cols];
dOutputs.Gather(outputs);
Assert.AreEqual(inputs, outputs);
}
public static void Main(string[] args)
{
}
}
}

Customizing TwoDArrayWritable in Hadoop and Not able to iterate the same in reducer

Trying to emit 2 double dimensional array as value from mapper.
In hadoop we have TwoDArrayWritable which takes 1 - 2D array as input.
In order to achieve my usecase, I tried to edit TwoDArrayWritable to take input of 2 - 2D array
/**
* A Writable for 2D arrays containing a matrix of instances of a class.
*/
public class MyTwoDArrayWritable implements Writable {
private Class valueClass;
private Writable[][] values;
private Class valueClass1;
private Writable[][] values1;
public MyTwoDArrayWritable(Class valueClass,Class valueClass1) {
this.valueClass = valueClass;
this.valueClass1 = valueClass1;
}
public MyTwoDArrayWritable(Class valueClass, DoubleWritable[][] values,Class valueClass1, DoubleWritable[][] values1) {
this(valueClass, valueClass1);
this.values = values;
this.values1 = values1;
}
public Object toArray() {
int dimensions[] = {values.length, 0};
Object result = Array.newInstance(valueClass, dimensions);
for (int i = 0; i < values.length; i++) {
Object resultRow = Array.newInstance(valueClass, values[i].length);
Array.set(result, i, resultRow);
for (int j = 0; j < values[i].length; j++) {
Array.set(resultRow, j, values[i][j]);
}
}
return result;
}
/**
* #return the valueClass
*/
public Class getValueClass() {
return valueClass;
}
/**
* #param valueClass the valueClass to set
*/
public void setValueClass(Class valueClass) {
this.valueClass = valueClass;
}
/**
* #return the values
*/
public Writable[][] getValues() {
return values;
}
/**
* #param values the values to set
*/
public void setValues(DoubleWritable[][] values,DoubleWritable[][] values1) {
this.values = values;
this.values = values1;
}
/**
* #return the valueClass1
*/
public Class getValueClass1() {
return valueClass1;
}
/**
* #param valueClass1 the valueClass1 to set
*/
public void setValueClass1(Class valueClass1) {
this.valueClass1 = valueClass1;
}
/**
* #return the values1
*/
public Writable[][] getValues1() {
return values1;
}
public void readFields(DataInput in) throws IOException {
// construct matrix
values = new Writable[in.readInt()][];
for (int i = 0; i < values.length; i++) {
values[i] = new Writable[in.readInt()];
}
// construct values
for (int i = 0; i < values.length; i++) {
for (int j = 0; j < values[i].length; j++) {
Writable value; // construct value
try {
value = (Writable) valueClass.newInstance();
} catch (InstantiationException e) {
throw new RuntimeException(e.toString());
} catch (IllegalAccessException e) {
throw new RuntimeException(e.toString());
}
value.readFields(in); // read a value
values[i][j] = value; // store it in values
}
}
}
public void write(DataOutput out) throws IOException {
out.writeInt(values.length); // write values
for (int i = 0; i < values.length; i++) {
out.writeInt(values[i].length);
}
for (int i = 0; i < values.length; i++) {
for (int j = 0; j < values[i].length; j++) {
values[i][j].write(out);
}
}
}
}
And emited 2 2D double array from mapper.
MyTwoDArrayWritable array = new MyTwoDArrayWritable (DoubleWritable.class,DoubleWritable.class);
DoubleWritable[][] myInnerArray = new DoubleWritable[EtransEkey.length][EtransEkey[0].length];
DoubleWritable[][] myInnerArray1 = new DoubleWritable[EtransDevalue.length][EtransDevalue[0].length];
// set values in myInnerArray
for (int k1 = 0; k1 < EtransEkey.length; k1++) {
for(int j1=0;j1< EtransEkey[0].length;j1++){
myInnerArray[k1][j1] = new DoubleWritable(EtransEkey[k1][j1]);
}
}
for (int k1 = 0; k1 < EtransDevalue.length; k1++) {
for(int j1=0;j1< EtransDevalue[0].length;j1++){
myInnerArray1[k1][j1] = new DoubleWritable(EtransDevalue[k1][j1]);
}
}
array.set(myInnerArray,myInnerArray1);
Showing error in array.set(myInnerArray,myInnerArray1);
/*
* The method set(DoubleWritable[][], DoubleWritable[][]) is undefined for the type MyTwoDArrayWritableritable
*/
EDIT: How to iterate through these values in Reducer to get myInnerArray matrix and myInnerArray1 matrix?
So far what I did is
for (MyTwoDArrayWritable c : values) {
System.out.println(c.getValues());
DoubleWritable[][] myInnerArray = new DoubleWritable[KdimRow][KdimCol];
for (int k1 = 0; k1 < KdimRow; k1++) {
for(int j1=0;j1< KdimCol;j1++){
myInnerArray[k1][j1] = new DoubleWritable();
}
}
But how to store them back to a double array?
You have not defined the set method in MyTwoDArrayWritable, that is why that error is shown. Instead of calling array.set, you should use the method you have already defined which does exactly what you need: setValues, so replace
array.set(myInnerArray,myInnerArray1);
with
array.setValues(myInnerArray,myInnerArray1);

Objects stuck at top of stage and won't fall down

I am using math.random to randomly drop objects from the top of the stage. I had it working with one object. But as I wanted to increase the number to 6 objects, I added the following code: But I am "stuck" and so are the 6 objects at the top of the stage. What am I doing wrong here? I appreciate the help.
private function bombInit(): void {
roachBombArray = new Array();
for (var i:uint =0; i < numBombs; i++) {
roachBomb= new RoachBomb();
roachBomb.x = Math.random() * stage.stageWidth;
roachBomb.vy = Math.random() * 2 -1;
roachBomb.y = -10;
addChild(roachBomb);
roachBombArray.push(roachBomb);
}
addEventListener(Event.ENTER_FRAME, onEntry);
}
private function onEntry(event:Event):void {
for (var i:uint = 0; i< numBombs; i++) {
var roachBomb = roachBombArray[i];
vy += ay;
roachBombArray[i] += vy;
if (roachBombArray[i] > 620) {
removeChild(roachBombArray[i]);
removeEventListener(Event.ENTER_FRAME, onEntry);
You are trying to add the velocity to the RoachBomb rather than to the RoachBomb y position.
roachBombArray[i] += vy;
should be
roachBombArray[i].y += vy;
Additionally you create a local variable:
var roachBomb = roachBombArray[i];
but you never manipulate it.
Perhaps you meant to do something like this?
var roachBomb:RoachBomb = roachBombArray[i]; // I added the type to the local variable
roachBomb.vy += ay;
roachBomb.y += vy; // Manipulate the local variable
if (roachBomb.y > 620) {
removeChild(roachBomb);
}
You're removing your enterFrame listener when the first bomb goes off the bottom, at which point you're no longer listening for ENTER_FRAME events and updating any of your bombs.
You don't want to remove this listener until you're done animating ALL the bombs.
UPDATE: How I would expect things to look, incorperating Ethan's observation that you ought to use the local roachBomb that you declare...
public class BombDropper extends Sprite {
private static const GRAVITY:int = 1; // Set gravity to what you want in pixels/frame^2
private static const BOTTOM_OF_SCREEN:int = 620;
private var numBombs:int = 6;
private var roachBombArray:Array;
// ... constructor and other class stuff here
private function bombInit(): void
{
roachBombArray = new Array();
for (var i:int =0; i < numBombs; ++i)
{
var roachBomb:RoachBomb = new RoachBomb();
roachBomb.x = Math.random() * stage.stageWidth;
roachBomb.vy = Math.random() * 2 -1;
roachBomb.y = -10;
this.addChild(roachBomb);
roachBombArray.push(roachBomb);
}
this.addEventListener(Event.ENTER_FRAME, onEntry);
}
private function onEntry(event:Event):void
{
for each ( var roachBomb:RoachBomb in roachBombArray)
{
roachBomb.vy += GRAVITY;
roachBomb.y += vy;
if (roachBomb.y > BOTTOM_OF_SCREEN)
{
this.removeChild(roachBomb);
roachBombArray.splice(roachBombArray.indexOf(roachBomb),1);
if (roachBombArray.length == 0)
{
this.removeEventListener(Event.ENTER_FRAME, onEntry);
}
}
}
}
}

Resources