Browse Source

Start compatibility with Techblox

Added some TODOs as well
tags/v2.0.0
NorbiPeti 3 years ago
parent
commit
a6b69d94c9
Signed by: NorbiPeti <szatmari.norbert.peter@gmail.com> GPG Key ID: DBA4C4549A927E56
17 changed files with 690 additions and 727 deletions
  1. +1
    -1
      Automation/gen_csproj.py
  2. +4
    -2
      GamecraftModdingAPI/App/GameGameEngine.cs
  3. +6
    -6
      GamecraftModdingAPI/App/GameMenuEngine.cs
  4. +0
    -11
      GamecraftModdingAPI/Block.cs
  5. +0
    -2
      GamecraftModdingAPI/Blocks/BlockCloneEngine.cs
  6. +3
    -3
      GamecraftModdingAPI/Blocks/BlockEngine.cs
  7. +1
    -1
      GamecraftModdingAPI/Blocks/BlockEngineInit.cs
  8. +0
    -75
      GamecraftModdingAPI/Blocks/ConsoleBlock.cs
  9. +2
    -2
      GamecraftModdingAPI/Blocks/CustomBlock.cs
  10. +2
    -2
      GamecraftModdingAPI/Blocks/MovementEngine.cs
  11. +22
    -20
      GamecraftModdingAPI/Blocks/PlacementEngine.cs
  12. +2
    -2
      GamecraftModdingAPI/Blocks/RotationEngine.cs
  13. +2
    -2
      GamecraftModdingAPI/Blocks/SignalEngine.cs
  14. +637
    -589
      GamecraftModdingAPI/GamecraftModdingAPI.csproj
  15. +4
    -5
      GamecraftModdingAPI/Interface/IMGUI/IMGUIManager.cs
  16. +2
    -2
      GamecraftModdingAPI/Persistence/SimpleEntitySerializer.cs
  17. +2
    -2
      GamecraftModdingAPI/Players/PlayerEngine.cs

+ 1
- 1
Automation/gen_csproj.py View File

@@ -32,7 +32,7 @@ if __name__ == "__main__":
args = parser.parse_args()
print("Building Assembly references")
asmXml = buildReferencesXml("../ref/Gamecraft_Data/Managed")
asmXml = buildReferencesXml("../ref/TechbloxPreview_Data/Managed")
# print(asmXml)
with open("../GamecraftModdingAPI/GamecraftModdingAPI.csproj", "r") as xmlFile:


+ 4
- 2
GamecraftModdingAPI/App/GameGameEngine.cs View File

@@ -13,6 +13,7 @@ using Svelto.Tasks.Lean;
using GamecraftModdingAPI.Blocks;
using GamecraftModdingAPI.Engines;
using GamecraftModdingAPI.Utility;
using RobocraftX.Blocks;

namespace GamecraftModdingAPI.App
{
@@ -97,7 +98,7 @@ namespace GamecraftModdingAPI.App

public EGID[] GetAllBlocksInGame(BlockIDs filter = BlockIDs.Invalid)
{
var allBlocks = entitiesDB.QueryEntities<DBEntityStruct>();
var allBlocks = entitiesDB.QueryEntities<BlockTagEntityStruct>();
List<EGID> blockEGIDs = new List<EGID>();
if (filter == BlockIDs.Invalid)
{
@@ -118,7 +119,8 @@ namespace GamecraftModdingAPI.App
for (var index = 0; index < array.capacity; index++)
{
var block = array[index];
if (block.DBID == (ulong) filter)
uint dbid = entitiesDB.QueryEntity<DBEntityStruct>(block.ID).DBID;
if (dbid == (ulong) filter)
blockEGIDs.Add(block.ID);
}
}


+ 6
- 6
GamecraftModdingAPI/App/GameMenuEngine.cs View File

@@ -44,7 +44,7 @@ namespace GamecraftModdingAPI.App

public bool CreateMyGame(EGID id, string path = "", uint thumbnailId = 0, string gameName = "", string creatorName = "", string description = "", long createdDate = 0L)
{
EntityComponentInitializer eci = Factory.BuildEntity<MyGameDataEntityDescriptor_DamnItFJWhyDidYouMakeThisInternal>(id);
EntityInitializer eci = Factory.BuildEntity<MyGameDataEntityDescriptor_DamnItFJWhyDidYouMakeThisInternal>(id);
eci.Init(new MyGameDataEntityStruct
{
SavedGamePath = new ECSString(path),
@@ -93,7 +93,7 @@ namespace GamecraftModdingAPI.App
{
if (!ExistsGameInfo(id)) return false;
GetGameInfo(id).GameName.Set(name);
GetGameViewInfo(id).MyGamesSlotComponent.GameName = StringUtil.SanitiseString(name);
//GetGameViewInfo(id).MyGamesSlotComponent.GameName = StringUtil.SanitiseString(name); - TODO: Input field struct
return true;
}

@@ -101,7 +101,7 @@ namespace GamecraftModdingAPI.App
{
if (!ExistsGameInfo(id)) return false;
GetGameInfo(id).GameDescription.Set(name);
GetGameViewInfo(id).MyGamesSlotComponent.GameDescription = StringUtil.SanitiseString(name);
//GetGameViewInfo(id).MyGamesSlotComponent.GameDescription = StringUtil.SanitiseString(name); - TODO
return true;
}

@@ -115,7 +115,7 @@ namespace GamecraftModdingAPI.App
return ref GetComponent<MyGameDataEntityStruct>(id);
}

public ref MyGamesSlotEntityViewStruct GetGameViewInfo(EGID id)
/*public ref MyGamesSlotEntityViewStruct GetGameViewInfo(EGID id)
{
EntityCollection<MyGamesSlotEntityViewStruct> entities =
entitiesDB.QueryEntities<MyGamesSlotEntityViewStruct>(MyGamesScreenExclusiveGroups.GameSlotGuiEntities);
@@ -128,8 +128,8 @@ namespace GamecraftModdingAPI.App
}
}
MyGamesSlotEntityViewStruct[] defRef = new MyGamesSlotEntityViewStruct[1];
return ref defRef[0];
}
return ref defRef[0]; - TODO: The struct is internal now
}*/

public ref T GetComponent<T>(EGID id) where T: unmanaged, IEntityComponent
{


+ 0
- 11
GamecraftModdingAPI/Block.cs View File

@@ -117,7 +117,6 @@ namespace GamecraftModdingAPI
private static Dictionary<Type, ExclusiveBuildGroup[]> typeToGroup =
new Dictionary<Type, ExclusiveBuildGroup[]>
{
{typeof(ConsoleBlock), new[] {CommonExclusiveGroups.CONSOLE_BLOCK_GROUP}},
{typeof(LogicGate), new [] {CommonExclusiveGroups.LOGIC_BLOCK_GROUP}},
{typeof(Motor), new[] {CommonExclusiveGroups.MOTOR_BLOCK_GROUP}},
{typeof(MusicBlock), new[] {CommonExclusiveGroups.MUSIC_BLOCK_GROUP}},
@@ -436,16 +435,6 @@ namespace GamecraftModdingAPI
{
var block = PlaceNew<T>(Type, Position, Rotation, Color.Color, Color.Darkness, UniformScale, Scale);
block.copiedFrom = Id;
if (Type == BlockIDs.ConsoleBlock
&& (this is ConsoleBlock srcCB || (srcCB = Specialise<ConsoleBlock>()) != null)
&& (block is ConsoleBlock dstCB || (dstCB = block.Specialise<ConsoleBlock>()) != null))
{
//Console block properties are set by a separate engine in the game
dstCB.Arg1 = srcCB.Arg1;
dstCB.Arg2 = srcCB.Arg2;
dstCB.Arg3 = srcCB.Arg3;
dstCB.Command = srcCB.Command;
}
return block;
}



+ 0
- 2
GamecraftModdingAPI/Blocks/BlockCloneEngine.cs View File

@@ -48,7 +48,6 @@ namespace GamecraftModdingAPI.Blocks
var oldStruct = pickedBlock;
pickedBlock.pickedBlockEntityID = sourceID;
pickedBlock.placedBlockEntityID = targetID;
pickedBlock.placedBlockTweaksCopied = false;
pickedBlock.placedBlockTweaksMustCopy = true;
if (entitiesDB.Exists<DBEntityStruct>(pickedBlock.pickedBlockEntityID)
&& entitiesDB.Exists<DBEntityStruct>(pickedBlock.placedBlockEntityID))
@@ -66,7 +65,6 @@ namespace GamecraftModdingAPI.Blocks
copyWireToBlock.Invoke(Patch.createWireEngine, new object[] {group, pickedBlock.ID});
pickedBlock.placedBlockTweaksMustCopy = false;
pickedBlock.placedBlockTweaksCopied = false;
}

pickedBlock = oldStruct; //Make sure to not interfere with the game - Although that might not be the case with the wire copying


+ 3
- 3
GamecraftModdingAPI/Blocks/BlockEngine.cs View File

@@ -114,7 +114,7 @@ namespace GamecraftModdingAPI.Blocks
private U GetBlockInitInfo<T, U>(Block block, Func<T, U> getter, U def) where T : struct, IEntityComponent
{
if (block.InitData.Group == null) return def;
var initializer = new EntityComponentInitializer(block.Id, block.InitData.Group);
var initializer = new EntityInitializer(block.Id, block.InitData.Group);
if (initializer.Has<T>())
return getter(initializer.Get<T>());
return def;
@@ -143,7 +143,7 @@ namespace GamecraftModdingAPI.Blocks
{
if (block.InitData.Group != null)
{
var initializer = new EntityComponentInitializer(block.Id, block.InitData.Group);
var initializer = new EntityInitializer(block.Id, block.InitData.Group);
T component = initializer.Has<T>() ? initializer.Get<T>() : default;
ref T structRef = ref component;
setter(ref structRef, value);
@@ -171,7 +171,7 @@ namespace GamecraftModdingAPI.Blocks
return true;
if (block.InitData.Group == null)
return false;
var init = new EntityComponentInitializer(block.Id, block.InitData.Group);
var init = new EntityInitializer(block.Id, block.InitData.Group);
return init.Has<T>();
}



+ 1
- 1
GamecraftModdingAPI/Blocks/BlockEngineInit.cs View File

@@ -18,7 +18,7 @@ namespace GamecraftModdingAPI.Blocks
}

internal delegate FasterDictionary<RefWrapperType, ITypeSafeDictionary> GetInitGroup(
EntityComponentInitializer initializer);
EntityInitializer initializer);

/// <summary>
/// Accesses the group field of the initializer


+ 0
- 75
GamecraftModdingAPI/Blocks/ConsoleBlock.cs View File

@@ -1,75 +0,0 @@
using System;

using RobocraftX.Blocks;
using RobocraftX.Common;
using Svelto.ECS;
using Unity.Mathematics;

using GamecraftModdingAPI;
using GamecraftModdingAPI.Utility;

namespace GamecraftModdingAPI.Blocks
{
public class ConsoleBlock : SignalingBlock
{
public ConsoleBlock(EGID id): base(id)
{
}

public ConsoleBlock(uint id): base(new EGID(id, CommonExclusiveGroups.CONSOLE_BLOCK_GROUP))
{
}

// custom console block properties

/// <summary>
/// Setting a nonexistent command will crash the game when switching to simulation
/// </summary>
public string Command
{
get
{
return BlockEngine.GetBlockInfo(this, (ConsoleBlockEntityStruct st) => st.commandName);
}

set
{
BlockEngine.SetBlockInfo(this, (ref ConsoleBlockEntityStruct st, string val) => st.commandName.Set(val),
value);
}
}

public string Arg1
{
get => BlockEngine.GetBlockInfo(this, (ConsoleBlockEntityStruct st) => st.arg1);

set
{
BlockEngine.SetBlockInfo(this, (ref ConsoleBlockEntityStruct st, string val) => st.arg1.Set(val),
value);
}
}

public string Arg2
{
get => BlockEngine.GetBlockInfo(this, (ConsoleBlockEntityStruct st) => st.arg2);

set
{
BlockEngine.SetBlockInfo(this, (ref ConsoleBlockEntityStruct st, string val) => st.arg2.Set(val),
value);
}
}

public string Arg3
{
get => BlockEngine.GetBlockInfo(this, (ConsoleBlockEntityStruct st) => st.arg3);

set
{
BlockEngine.SetBlockInfo(this, (ref ConsoleBlockEntityStruct st, string val) => st.arg3.Set(val),
value);
}
}
}
}

+ 2
- 2
GamecraftModdingAPI/Blocks/CustomBlock.cs View File

@@ -6,6 +6,7 @@ using System.Reflection;
using HarmonyLib;

using DataLoader;
using RobocraftX.Common;
using RobocraftX.Rendering;
using RobocraftX.Schedulers;
using Svelto.ECS;
@@ -130,8 +131,7 @@ namespace GamecraftModdingAPI.Blocks
CubeDescriptionKey = attr.DescKey,
SelectableFaces = new[] {0, 1, 2, 3, 4, 5},
GridScale = new[] {5, 5, 5},
Mass = attr.Mass,
Material = attr.Material,
DefaultMaterialID = 0, //TODO: Material API
scalingPermission = attr.ScalingPermission,
SortIndex = attr.SortIndex,
DefaultColour = attr.DefaultColor.Index,


+ 2
- 2
GamecraftModdingAPI/Blocks/MovementEngine.cs View File

@@ -40,7 +40,7 @@ namespace GamecraftModdingAPI.Blocks
if (!entitiesDB.Exists<PositionEntityStruct>(blockID))
{
if (data.Group == null) return float3.zero;
var init = new EntityComponentInitializer(blockID, data.Group);
var init = new EntityInitializer(blockID, data.Group);
init.GetOrCreate<PositionEntityStruct>().position = vector;
init.GetOrCreate<GridRotationStruct>().position = vector;
init.GetOrCreate<LocalTransformEntityStruct>().position = vector;
@@ -70,7 +70,7 @@ namespace GamecraftModdingAPI.Blocks
if (!entitiesDB.Exists<PositionEntityStruct>(blockID))
{
if (data.Group == null) return float3.zero;
var init = new EntityComponentInitializer(blockID, data.Group);
var init = new EntityInitializer(blockID, data.Group);
return init.Has<PositionEntityStruct>() ? init.Get<PositionEntityStruct>().position : float3.zero;
}
ref PositionEntityStruct posStruct = ref this.entitiesDB.QueryEntity<PositionEntityStruct>(blockID);


+ 22
- 20
GamecraftModdingAPI/Blocks/PlacementEngine.cs View File

@@ -38,10 +38,10 @@ namespace GamecraftModdingAPI.Blocks
}

public EntitiesDB entitiesDB { get; set; }
private static BlockEntityFactory _blockEntityFactory; //Injected from PlaceBlockEngine
private static BlockEntityFactory _blockEntityFactory; //Injected from PlaceSingleBlockEngine

public EGID PlaceBlock(BlockIDs block, BlockColors color, byte darkness, float3 position, int uscale,
float3 scale, Player player, float3 rotation, out EntityComponentInitializer initializer)
float3 scale, Player player, float3 rotation, out EntityInitializer initializer)
{ //It appears that only the non-uniform scale has any visible effect, but if that's not given here it will be set to the uniform one
if (darkness > 9)
throw new Exception("That is too dark. Make sure to use 0-9 as darkness. (0 is default.)");
@@ -50,7 +50,7 @@ namespace GamecraftModdingAPI.Blocks
return initializer.EGID;
}

private EntityComponentInitializer BuildBlock(ushort block, byte color, float3 position, int uscale, float3 scale, float3 rot, uint playerId)
private EntityInitializer BuildBlock(ushort block, byte color, float3 position, int uscale, float3 scale, float3 rot, uint playerId)
{
if (_blockEntityFactory == null)
throw new BlockException("The factory is null.");
@@ -59,32 +59,34 @@ namespace GamecraftModdingAPI.Blocks
if (scale.x < 4e-5) scale.x = uscale;
if (scale.y < 4e-5) scale.y = uscale;
if (scale.z < 4e-5) scale.z = uscale;
uint dbid = block;
if (!PrefabsID.HasPrefabRegistered(dbid, 0))
throw new BlockException("Block with ID " + dbid + " not found!");
//RobocraftX.CR.MachineEditing.PlaceBlockEngine
uint resourceId = (uint) PrefabsID.GenerateResourceID(0, block);
if (!PrefabsID.PrefabIDByResourceIDMap.ContainsKey(resourceId))
throw new BlockException("Block with ID " + block + " not found!");
//RobocraftX.CR.MachineEditing.PlaceSingleBlockEngine
ScalingEntityStruct scaling = new ScalingEntityStruct {scale = scale};
Quaternion rotQ = Quaternion.Euler(rot);
RotationEntityStruct rotation = new RotationEntityStruct {rotation = rotQ};
GridRotationStruct gridRotation = new GridRotationStruct
{position = position, rotation = rotQ};
DBEntityStruct dbEntity = new DBEntityStruct {DBID = dbid};
DBEntityStruct dbEntity = new DBEntityStruct {DBID = block};
BlockPlacementScaleEntityStruct placementScale = new BlockPlacementScaleEntityStruct
{
blockPlacementHeight = uscale, blockPlacementWidth = uscale, desiredScaleFactor = uscale
};
EquippedColourStruct colour = new EquippedColourStruct {indexInPalette = color};

EntityComponentInitializer
structInitializer =
_blockEntityFactory.Build(CommonExclusiveGroups.nextBlockEntityID, dbid); //The ghost block index is only used for triggers
if (colour.indexInPalette != byte.MaxValue)
EntityInitializer structInitializer = _blockEntityFactory.Build(CommonExclusiveGroups.nextBlockEntityID, block); //The ghost block index is only used for triggers
if (color != byte.MaxValue)
structInitializer.Init(new ColourParameterEntityStruct
{
indexInPalette = colour.indexInPalette,
indexInPalette = color,
hasNetworkChange = true
});
uint prefabId = PrefabsID.GetPrefabId(dbid, 0);
structInitializer.Init(new CubeMaterialStruct
{
materialId = 0, //TODO
cosmeticallyPaintedOnly = true //TODO
});
uint prefabId = PrefabsID.GetOrCreatePrefabID(block, 0, 0, false); //TODO
structInitializer.Init(new GFXPrefabEntityStructGPUI(prefabId));
structInitializer.Init(new PhysicsPrefabEntityStruct(prefabId));
structInitializer.Init(dbEntity);
@@ -99,16 +101,16 @@ namespace GamecraftModdingAPI.Blocks
structInitializer.Init(new BlockPlacementInfoStruct()
{
loadedFromDisk = false,
placedBy = playerId
placedBy = playerId,
triggerAutoWiring = false //TODO
});
/*structInitializer.Init(new CollisionFilterOverride
{
belongsTo = 32U,
collidesWith = 239532U
});*/

PrimaryRotationUtility.InitialisePrimaryDirection(rotation.rotation, ref structInitializer);
EGID playerEGID = new EGID(playerId, CharacterExclusiveGroups.OnFootGroup);
ref PickedBlockExtraDataStruct pickedBlock = ref entitiesDB.QueryEntity<PickedBlockExtraDataStruct>(playerEGID);
pickedBlock.placedBlockEntityID = structInitializer.EGID;
@@ -118,7 +120,7 @@ namespace GamecraftModdingAPI.Blocks

public string Name { get; } = "GamecraftModdingAPIPlacementGameEngine";

public bool isRemovable => false;
public bool isRemovable => false;

[HarmonyPatch]
public class FactoryObtainerPatch
@@ -131,7 +133,7 @@ namespace GamecraftModdingAPI.Blocks

static MethodBase TargetMethod(Harmony instance)
{
return AccessTools.TypeByName("RobocraftX.CR.MachineEditing.PlaceBlockEngine").GetConstructors()[0];
return AccessTools.TypeByName("RobocraftX.CR.MachineEditing.PlaceSingleBlockEngine").GetConstructors()[0];
}
}
}

+ 2
- 2
GamecraftModdingAPI/Blocks/RotationEngine.cs View File

@@ -40,7 +40,7 @@ namespace GamecraftModdingAPI.Blocks
if (!entitiesDB.Exists<RotationEntityStruct>(blockID))
{
if (data.Group == null) return float3.zero;
var init = new EntityComponentInitializer(blockID, data.Group);
var init = new EntityInitializer(blockID, data.Group);
init.GetOrCreate<RotationEntityStruct>().rotation = Quaternion.Euler(vector);
init.GetOrCreate<GridRotationStruct>().rotation = Quaternion.Euler(vector);
init.GetOrCreate<LocalTransformEntityStruct>().rotation = Quaternion.Euler(vector);
@@ -77,7 +77,7 @@ namespace GamecraftModdingAPI.Blocks
if (!entitiesDB.Exists<RotationEntityStruct>(blockID))
{
if (data.Group == null) return float3.zero;
var init = new EntityComponentInitializer(blockID, data.Group);
var init = new EntityInitializer(blockID, data.Group);
return init.Has<RotationEntityStruct>()
? (float3) ((Quaternion) init.Get<RotationEntityStruct>().rotation).eulerAngles
: float3.zero;


+ 2
- 2
GamecraftModdingAPI/Blocks/SignalEngine.cs View File

@@ -42,7 +42,7 @@ namespace GamecraftModdingAPI.Blocks
public WireEntityStruct CreateNewWire(EGID startBlock, byte startPort, EGID endBlock, byte endPort)
{
EGID wireEGID = new EGID(WiresExclusiveGroups.NewWireEntityId, NamedExclusiveGroup<WiresGroup>.Group);
EntityComponentInitializer wireInitializer = Factory.BuildEntity<WireEntityDescriptor>(wireEGID);
EntityInitializer wireInitializer = Factory.BuildEntity<WireEntityDescriptor>(wireEGID);
wireInitializer.Init(new WireEntityStruct
{
sourceBlockEGID = startBlock,
@@ -399,7 +399,7 @@ namespace GamecraftModdingAPI.Blocks
exists = false;
return ref defRef[0];
}
EntityComponentInitializer initializer = new EntityComponentInitializer(block.Id, block.InitData.Group);
EntityInitializer initializer = new EntityInitializer(block.Id, block.InitData.Group);
if (initializer.Has<T>())
{
exists = true;


+ 637
- 589
GamecraftModdingAPI/GamecraftModdingAPI.csproj
File diff suppressed because it is too large
View File


+ 4
- 5
GamecraftModdingAPI/Interface/IMGUI/IMGUIManager.cs View File

@@ -22,7 +22,7 @@ namespace GamecraftModdingAPI.Interface.IMGUI
{
internal static OnGuiRunner ImguiScheduler = new OnGuiRunner("GamecraftModdingAPI_IMGUIScheduler");
private static FasterDictionary<string, UIElement> _activeElements = new FasterDictionary<string,UIElement>();
private static Dictionary<string, UIElement> _activeElements = new Dictionary<string,UIElement>();

/// <summary>
/// Add an UIElement instance to be managed by IMGUIManager.
@@ -85,11 +85,10 @@ namespace GamecraftModdingAPI.Interface.IMGUI

private static void OnGUI()
{
UIElement[] elements = _activeElements.GetValuesArray(out uint count);
for(uint i = 0; i < count; i++)
foreach (var element in _activeElements.Values)
{
if (elements[i].Enabled)
elements[i].OnGUI();
if (element.Enabled)
element.OnGUI();
/*try
{
if (elements[i].Enabled)


+ 2
- 2
GamecraftModdingAPI/Persistence/SimpleEntitySerializer.cs View File

@@ -23,9 +23,9 @@ namespace GamecraftModdingAPI.Persistence

public EntitiesDB entitiesDB { set; protected get; }

public EntityComponentInitializer BuildDeserializedEntity(EGID egid, ISerializationData serializationData, ISerializableEntityDescriptor entityDescriptor, int serializationType, IEntitySerialization entitySerialization, IEntityFactory factory, bool enginesRootIsDeserializationOnly)
public EntityInitializer BuildDeserializedEntity(EGID egid, ISerializationData serializationData, ISerializableEntityDescriptor entityDescriptor, int serializationType, IEntitySerialization entitySerialization, IEntityFactory factory, bool enginesRootIsDeserializationOnly)
{
EntityComponentInitializer esi = factory.BuildEntity<Descriptor>(egid);
EntityInitializer esi = factory.BuildEntity<Descriptor>(egid);
entitySerialization.DeserializeEntityComponents(serializationData, entityDescriptor, ref esi, serializationType);
return esi;
}


+ 2
- 2
GamecraftModdingAPI/Players/PlayerEngine.cs View File

@@ -10,10 +10,10 @@ using RobocraftX.Common.Input;
using RobocraftX.CR.MachineEditing.BoxSelect;
using RobocraftX.Physics;
using RobocraftX.Blocks.Ghost;
using RobocraftX.Character.Camera;
using RobocraftX.Character.Factories;
using Gamecraft.GUI.HUDFeedbackBlocks;
using Svelto.ECS;
using Techblox.Camera;
using Techblox.FlyCam;
using Unity.Mathematics;
using Unity.Physics;
using UnityEngine;


Loading…
Cancel
Save